From 76d71e8d6b0606d47618ffe0aadb9e2052593338 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Fri, 17 Jul 2026 14:27:43 -0500 Subject: [PATCH 1/6] Add static generic caches for reducers and more --- .../diag/snapshots/Module#FFI.verified.cs | 344 +++++++++++++++--- .../snapshots/Module#FFI.verified.cs | 141 ++++--- .../server/snapshots/Module#FFI.verified.cs | 211 ++++++++--- crates/bindings-csharp/Codegen/Module.cs | 138 ++++--- .../Runtime/Internal/Module.cs | 221 ++++++++++- 5 files changed, 859 insertions(+), 196 deletions(-) diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index c5fbd45e8b9..f6bba9200a8 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -3210,7 +3210,7 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { - class __ReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer + sealed class __ReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3231,7 +3231,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class DummyScheduledReducer : SpacetimeDB.Internal.IReducer + sealed class DummyScheduledReducer : SpacetimeDB.Internal.IReducer { private static readonly TestScheduleIssues.BSATN tableRW = new(); @@ -3257,7 +3257,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class OnReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer + sealed class OnReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3278,7 +3278,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestDuplicateReducerKind1 : SpacetimeDB.Internal.IReducer + sealed class TestDuplicateReducerKind1 : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3299,7 +3299,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestDuplicateReducerKind2 : SpacetimeDB.Internal.IReducer + sealed class TestDuplicateReducerKind2 : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3320,7 +3320,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestDuplicateReducerName : SpacetimeDB.Internal.IReducer + sealed class TestDuplicateReducerName : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3341,7 +3341,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestReducerReturnType : SpacetimeDB.Internal.IReducer + sealed class TestReducerReturnType : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3362,7 +3362,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestReducerWithoutContext : SpacetimeDB.Internal.IReducer + sealed class TestReducerWithoutContext : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -3756,18 +3756,118 @@ public static SpacetimeDB.Internal.Errno __call_reducer__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink error ) => - SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_reducer__<__ReducerWithReservedPrefix>( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 1 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 2 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 3 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 4 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 5 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 6 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 7 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + _ + => SpacetimeDB.Internal.Module.__call_reducer__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] public static SpacetimeDB.Internal.Errno __call_procedure__( @@ -3782,18 +3882,22 @@ public static SpacetimeDB.Internal.Errno __call_procedure__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink result_sink ) => - SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ); + id switch + { + _ + => SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] public static SpacetimeDB.Internal.Errno __call_http_handler__( @@ -3804,14 +3908,18 @@ public static SpacetimeDB.Internal.Errno __call_http_handler__( SpacetimeDB.Internal.BytesSink response_sink, SpacetimeDB.Internal.BytesSink response_body_sink ) => - SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ); + id switch + { + _ + => SpacetimeDB.Internal.Module.__call_http_handler__( + id, + timestamp, + request, + request_body, + response_sink, + response_body_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( @@ -3823,22 +3931,158 @@ public static SpacetimeDB.Internal.Errno __call_view__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink ) => - SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 1 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 2 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 3 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 4 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 5 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 6 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 7 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 8 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 9 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 10 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 11 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + _ + => SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink - ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink); + ) => + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_view_anon__( + args, + sink + ), + 1 + => SpacetimeDB.Internal.Module.__call_view_anon__( + args, + sink + ), + 2 + => SpacetimeDB.Internal.Module.__call_view_anon__( + args, + sink + ), + 3 + => SpacetimeDB.Internal.Module.__call_view_anon__( + args, + sink + ), + _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + }; #endif } diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs index e0124d76783..3b5bcc02f65 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs @@ -511,7 +511,7 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { - class DemoReducer : SpacetimeDB.Internal.IReducer + sealed class DemoReducer : SpacetimeDB.Internal.IReducer { private static readonly SpacetimeDB.BSATN.I32 valueRW = new(); @@ -534,7 +534,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class DemoProcedure : SpacetimeDB.Internal.IProcedure + sealed class DemoProcedure : SpacetimeDB.Internal.IProcedure { public SpacetimeDB.Internal.RawProcedureDefV10 MakeProcedureDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -646,18 +646,34 @@ public static SpacetimeDB.Internal.Errno __call_reducer__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink error ) => - SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + _ + => SpacetimeDB.Internal.Module.__call_reducer__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] public static SpacetimeDB.Internal.Errno __call_procedure__( @@ -672,18 +688,34 @@ public static SpacetimeDB.Internal.Errno __call_procedure__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink result_sink ) => - SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_procedure__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ), + _ + => SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] public static SpacetimeDB.Internal.Errno __call_http_handler__( @@ -694,14 +726,18 @@ public static SpacetimeDB.Internal.Errno __call_http_handler__( SpacetimeDB.Internal.BytesSink response_sink, SpacetimeDB.Internal.BytesSink response_body_sink ) => - SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ); + id switch + { + _ + => SpacetimeDB.Internal.Module.__call_http_handler__( + id, + timestamp, + request, + request_body, + response_sink, + response_body_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( @@ -713,22 +749,39 @@ public static SpacetimeDB.Internal.Errno __call_view__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink ) => - SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + _ + => SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink - ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink); + ) => + id switch + { + _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + }; #endif } diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index 234e43c9c50..3378de69b21 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -2322,7 +2322,7 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { - class Init : SpacetimeDB.Internal.IReducer + sealed class Init : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( SpacetimeDB.BSATN.ITypeRegistrar registrar @@ -2343,7 +2343,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class InsertData : SpacetimeDB.Internal.IReducer + sealed class InsertData : SpacetimeDB.Internal.IReducer { private static readonly PublicTable.BSATN dataRW = new(); @@ -2366,7 +2366,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class InsertData2 : SpacetimeDB.Internal.IReducer + sealed class InsertData2 : SpacetimeDB.Internal.IReducer { private static readonly PublicTable.BSATN dataRW = new(); @@ -2392,7 +2392,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class InsertMultiData : SpacetimeDB.Internal.IReducer + sealed class InsertMultiData : SpacetimeDB.Internal.IReducer { private static readonly MultiTableRow.BSATN dataRW = new(); @@ -2415,7 +2415,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class ScheduleImmediate : SpacetimeDB.Internal.IReducer + sealed class ScheduleImmediate : SpacetimeDB.Internal.IReducer { private static readonly PublicTable.BSATN dataRW = new(); @@ -2438,7 +2438,7 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class SendScheduledMessage : SpacetimeDB.Internal.IReducer + sealed class SendScheduledMessage : SpacetimeDB.Internal.IReducer { private static readonly Timers.SendMessageTimer.BSATN argRW = new(); @@ -2574,18 +2574,94 @@ public static SpacetimeDB.Internal.Errno __call_reducer__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink error ) => - SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 1 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 2 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 3 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 4 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 5 + => SpacetimeDB.Internal.Module.__call_reducer__( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + _ + => SpacetimeDB.Internal.Module.__call_reducer__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] public static SpacetimeDB.Internal.Errno __call_procedure__( @@ -2600,18 +2676,22 @@ public static SpacetimeDB.Internal.Errno __call_procedure__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink result_sink ) => - SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ); + id switch + { + _ + => SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] public static SpacetimeDB.Internal.Errno __call_http_handler__( @@ -2622,14 +2702,18 @@ public static SpacetimeDB.Internal.Errno __call_http_handler__( SpacetimeDB.Internal.BytesSink response_sink, SpacetimeDB.Internal.BytesSink response_body_sink ) => - SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ); + id switch + { + _ + => SpacetimeDB.Internal.Module.__call_http_handler__( + id, + timestamp, + request, + request_body, + response_sink, + response_body_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( @@ -2641,22 +2725,53 @@ public static SpacetimeDB.Internal.Errno __call_view__( SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink ) => - SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ); + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + 1 + => SpacetimeDB.Internal.Module.__call_view__( + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ), + _ + => SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink - ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink); + ) => + id switch + { + 0 + => SpacetimeDB.Internal.Module.__call_view_anon__( + args, + sink + ), + _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + }; #endif } diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 43bbc702687..5634ad21219 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1567,7 +1567,7 @@ public string GenerateClass() )})"; return $$""" - class {{Identifier}}: SpacetimeDB.Internal.IReducer { + sealed class {{Identifier}}: SpacetimeDB.Internal.IReducer { {{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args)}} public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new ( @@ -1797,7 +1797,7 @@ public string GenerateClass() } return $$$""" - class {{{Identifier}}} : SpacetimeDB.Internal.IProcedure { + sealed class {{{Identifier}}} : SpacetimeDB.Internal.IProcedure { {{{classFields}}} public SpacetimeDB.Internal.RawProcedureDefV10 MakeProcedureDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( @@ -1939,7 +1939,7 @@ public string GenerateClass() : $"return {FullName}((SpacetimeDB.HandlerContext)ctx, request);"; return $$""" - class {{Identifier}} : SpacetimeDB.Internal.IHttpHandler { + sealed class {{Identifier}} : SpacetimeDB.Internal.IHttpHandler { public SpacetimeDB.Internal.RawHttpHandlerDefV10 MakeHandlerDef() => new( SourceName: nameof({{Identifier}}) ); @@ -2906,18 +2906,26 @@ public static SpacetimeDB.Internal.Errno __call_reducer__( SpacetimeDB.Timestamp timestamp, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink error - ) => SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ); + ) => id switch { + {{string.Join( + "\n", + addReducers.Select((r, i) => + $"{i} => SpacetimeDB.Internal.Module.__call_reducer__<{EscapeIdentifier(r.Name)}>(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, error)," + ) + )}} + _ => SpacetimeDB.Internal.Module.__call_reducer__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] public static SpacetimeDB.Internal.Errno __call_procedure__( @@ -2931,18 +2939,26 @@ public static SpacetimeDB.Internal.Errno __call_procedure__( SpacetimeDB.Timestamp timestamp, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink result_sink - ) => SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ); + ) => id switch { + {{string.Join( + "\n", + addProcedures.Select((p, i) => + $"{i} => SpacetimeDB.Internal.Module.__call_procedure__<{EscapeIdentifier(p.Name)}>(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, result_sink)," + ) + )}} + _ => SpacetimeDB.Internal.Module.__call_procedure__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + result_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] public static SpacetimeDB.Internal.Errno __call_http_handler__( @@ -2952,14 +2968,22 @@ public static SpacetimeDB.Internal.Errno __call_http_handler__( SpacetimeDB.Internal.BytesSource request_body, SpacetimeDB.Internal.BytesSink response_sink, SpacetimeDB.Internal.BytesSink response_body_sink - ) => SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ); + ) => id switch { + {{string.Join( + "\n", + addHttpHandlers.Select((h, i) => + $"{i} => SpacetimeDB.Internal.Module.__call_http_handler__<{EscapeIdentifier(h.Name)}>(timestamp, request, request_body, response_sink, response_body_sink)," + ) + )}} + _ => SpacetimeDB.Internal.Module.__call_http_handler__( + id, + timestamp, + request, + request_body, + response_sink, + response_body_sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view__")] public static SpacetimeDB.Internal.Errno __call_view__( @@ -2970,26 +2994,42 @@ public static SpacetimeDB.Internal.Errno __call_view__( ulong sender_3, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink - ) => SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ); + ) => id switch { + {{string.Join("\n", + views.Array.Where(v => !v.IsAnonymous) + .Select((v, i) => + $"{i} => SpacetimeDB.Internal.Module.__call_view__<{v.Name}ViewDispatcher>(sender_0, sender_1, sender_2, sender_3, args, sink)," + ) + )}} + _ => SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ) + }; [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink sink - ) => SpacetimeDB.Internal.Module.__call_view_anon__( - id, - args, - sink - ); + ) => id switch { + {{string.Join("\n", + views.Array.Where(v => v.IsAnonymous) + .Select((v, i) => + $"{i} => SpacetimeDB.Internal.Module.__call_view_anon__<{v.Name}ViewDispatcher>(args, sink)," + ) + )}} + _ => SpacetimeDB.Internal.Module.__call_view_anon__( + id, + args, + sink + ) + }; #endif } diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index 83e04453964..e6fd46a265a 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -265,6 +265,36 @@ private static void EnsureNativeAotTypeRoots() private static readonly List viewDispatchers = []; private static readonly List anonymousViewDispatchers = []; + private static class ReducerCache + where R : IReducer, new() + { + public static readonly R Instance = new(); + } + + private static class ProcedureCache

+ where P : IProcedure, new() + { + public static readonly P Instance = new(); + } + + private static class HttpHandlerCache + where H : IHttpHandler, new() + { + public static readonly H Instance = new(); + } + + private static class ViewDispatcherCache + where TDispatcher : IView, new() + { + public static readonly TDispatcher Instance = new(); + } + + private static class AnonymousViewDispatcherCache + where TDispatcher : IAnonymousView, new() + { + public static readonly TDispatcher Instance = new(); + } + private static Func< Identity, ConnectionId?, @@ -337,7 +367,7 @@ public AlgebraicType.Ref RegisterType(Func public static void RegisterReducer() where R : IReducer, new() { - var reducer = new R(); + var reducer = ReducerCache.Instance; reducers.Add(reducer); moduleDef.RegisterReducer(reducer.MakeReducerDef(typeRegistrar), reducer.Lifecycle); } @@ -345,7 +375,7 @@ public static void RegisterReducer() public static void RegisterProcedure

() where P : IProcedure, new() { - var procedure = new P(); + var procedure = ProcedureCache

.Instance; procedures.Add(procedure); moduleDef.RegisterProcedure(procedure.MakeProcedureDef(typeRegistrar)); } @@ -353,7 +383,7 @@ public static void RegisterProcedure

() public static void RegisterHttpHandler() where H : IHttpHandler, new() { - var handler = new H(); + var handler = HttpHandlerCache.Instance; httpHandlers.Add(handler); moduleDef.RegisterHttpHandler(handler.MakeHandlerDef()); } @@ -394,7 +424,7 @@ public static void RegisterTable() public static void RegisterView() where TDispatcher : IView, new() { - var dispatcher = new TDispatcher(); + var dispatcher = ViewDispatcherCache.Instance; var def = dispatcher.MakeViewDef(typeRegistrar); viewDispatchers.Add(dispatcher); moduleDef.RegisterView(def); @@ -403,7 +433,7 @@ public static void RegisterView() public static void RegisterAnonymousView() where TDispatcher : IAnonymousView, new() { - var dispatcher = new TDispatcher(); + var dispatcher = AnonymousViewDispatcherCache.Instance; var def = dispatcher.MakeAnonymousViewDef(typeRegistrar); anonymousViewDispatchers.Add(dispatcher); moduleDef.RegisterView(def); @@ -579,6 +609,50 @@ BytesSink error } } + public static Errno __call_reducer__( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + Timestamp timestamp, + BytesSource args, + BytesSink error + ) + where R : IReducer, new() + { + try + { + var senderIdentity = Identity.From( + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() + ); + var connectionId = ConnectionId.From( + MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray() + ); + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); + + var ctx = newReducerContext!(senderIdentity, connectionId, random, time); + + using var stream = args.Consume(ref reducerArgsBuffer); + using var reader = new BinaryReader(stream); + ReducerCache.Instance.Invoke(reader, ctx); + if (stream.Position != stream.Length) + { + throw new Exception("Unrecognised extra bytes in the reducer arguments"); + } + return Errno.OK; /* no exception */ + } + catch (Exception e) + { + var error_str = e.Message ?? e.GetType().FullName ?? e.GetType().Name; + var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str); + error.Write(error_bytes); + return Errno.HOST_CALL_FAILURE; + } + } + public static Errno __call_procedure__( int id, ulong sender_0, @@ -624,6 +698,50 @@ BytesSink resultSink } } + public static Errno __call_procedure__

( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + Timestamp timestamp, + BytesSource args, + BytesSink resultSink + ) + where P : IProcedure, new() + { + try + { + var sender = Identity.From( + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() + ); + var connectionId = ConnectionId.From( + MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray() + ); + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); + + var ctx = newProcedureContext!(sender, connectionId, random, time); + + using var stream = args.Consume(ref procedureArgsBuffer); + using var reader = new BinaryReader(stream); + var bytes = ProcedureCache

.Instance.Invoke(reader, ctx); + if (stream.Position != stream.Length) + { + throw new Exception("Unrecognised extra bytes in the procedure arguments"); + } + resultSink.Write(bytes); + + return Errno.OK; + } + catch (Exception e) + { + Log.Error($"Error while invoking procedure: {e}"); + throw; + } + } + public static Errno __call_http_handler__( int id, Timestamp timestamp, @@ -668,6 +786,51 @@ BytesSink responseBodySink } } + public static Errno __call_http_handler__( + Timestamp timestamp, + BytesSource request, + BytesSource requestBody, + BytesSink responseSink, + BytesSink responseBodySink + ) + where H : IHttpHandler, new() + { + try + { + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); + var ctx = newHandlerContext!(random, time); + + using var stream = request.Consume(ref httpRequestBuffer); + using var reader = new BinaryReader(stream); + var requestWire = new HttpRequestWire.BSATN().Read(reader); + if (stream.Position != stream.Length) + { + throw new Exception("Unrecognised extra bytes in the HTTP handler request"); + } + + var response = HttpHandlerCache.Instance.Invoke( + ctx, + SpacetimeDB.HttpClient.FromWire( + requestWire, + requestBody.Consume(ref httpRequestBodyBuffer).ToArray() + ) + ); + var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); + responseSink.Write( + IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire) + ); + responseBodySink.Write(responseBody); + + return Errno.OK; + } + catch (Exception e) + { + Log.Error($"Error while invoking HTTP handler: {e}"); + throw; + } + } + ///

/// Called by the host to execute a view when the sender calls the view identified by . /// @@ -721,6 +884,35 @@ BytesSink rows } } + public static Errno __call_view__( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + BytesSource args, + BytesSink rows + ) + where TDispatcher : IView, new() + { + try + { + var sender = Identity.From( + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() + ); + var ctx = newViewContext!(sender); + using var stream = args.Consume(ref viewArgsBuffer); + using var reader = new BinaryReader(stream); + var bytes = ViewDispatcherCache.Instance.Invoke(reader, ctx); + rows.Write(bytes); + return (Errno)2; + } + catch (Exception e) + { + Log.Error($"Error while invoking view: {e}"); + return Errno.HOST_CALL_FAILURE; + } + } + /// /// Called by the host to execute an anonymous view. /// @@ -758,6 +950,25 @@ public static Errno __call_view_anon__(int id, BytesSource args, BytesSink rows) return Errno.HOST_CALL_FAILURE; } } + + public static Errno __call_view_anon__(BytesSource args, BytesSink rows) + where TDispatcher : IAnonymousView, new() + { + try + { + var ctx = newAnonymousViewContext!(); + using var stream = args.Consume(ref anonymousViewArgsBuffer); + using var reader = new BinaryReader(stream); + var bytes = AnonymousViewDispatcherCache.Instance.Invoke(reader, ctx); + rows.Write(bytes); + return (Errno)2; + } + catch (Exception e) + { + Log.Error($"Error while invoking anonymous view: {e}"); + return Errno.HOST_CALL_FAILURE; + } + } } /// From 610c8e05448e23d5c1af2b1d971d84e9cedb54d4 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Thu, 13 Aug 2026 10:42:57 -0500 Subject: [PATCH 2/6] Remove unnecessary ToArray() --- crates/bindings-csharp/Runtime/Internal/Module.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index e6fd46a265a..fca33f525f8 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -625,11 +625,9 @@ BytesSink error try { var senderIdentity = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() - ); - var connectionId = ConnectionId.From( - MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray() + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) ); + var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); var time = timestamp.ToStd(); @@ -714,11 +712,9 @@ BytesSink resultSink try { var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() - ); - var connectionId = ConnectionId.From( - MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray() + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) ); + var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); var time = timestamp.ToStd(); @@ -897,7 +893,7 @@ BytesSink rows try { var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray() + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) ); var ctx = newViewContext!(sender); using var stream = args.Consume(ref viewArgsBuffer); From d522f411f9fe8981a6a72b30a655d1a00242a01d Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Wed, 26 Aug 2026 09:24:02 -0500 Subject: [PATCH 3/6] Dispose the http request body stream in the generic path --- crates/bindings-csharp/Runtime/Internal/Module.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index fca33f525f8..bcebf97d59e 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -805,12 +805,10 @@ BytesSink responseBodySink throw new Exception("Unrecognised extra bytes in the HTTP handler request"); } + using var requestBodyStream = requestBody.Consume(ref httpRequestBodyBuffer); var response = HttpHandlerCache.Instance.Invoke( ctx, - SpacetimeDB.HttpClient.FromWire( - requestWire, - requestBody.Consume(ref httpRequestBodyBuffer).ToArray() - ) + SpacetimeDB.HttpClient.FromWire(requestWire, requestBodyStream.ToArray()) ); var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); responseSink.Write( From 2e78e4cb7cb4b9a990b21118125b5246526904d2 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Wed, 26 Aug 2026 13:16:11 -0500 Subject: [PATCH 4/6] Generate direct C# module dispatch for all build modes (AOT and non AOT) --- .../diag/snapshots/Module#FFI.verified.cs | 1267 +++++++++++++---- .../snapshots/Module#FFI.verified.cs | 218 ++- .../server/snapshots/Module#FFI.verified.cs | 458 ++++-- crates/bindings-csharp/Codegen/Module.cs | 247 +++- .../Runtime/Internal/IHttpHandler.cs | 5 - .../Runtime/Internal/IReducer.cs | 3 - .../bindings-csharp/Runtime/Internal/IView.cs | 6 - .../Runtime/Internal/Module.cs | 464 +----- .../Runtime/Internal/Procedure.cs | 5 - crates/bindings-csharp/Runtime/bindings.c | 12 +- 10 files changed, 1768 insertions(+), 917 deletions(-) diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index f6bba9200a8..6638bb12fc8 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -2101,7 +2101,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2151,7 +2151,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2195,7 +2195,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2237,7 +2237,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2279,7 +2279,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2319,7 +2319,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ReturnType: new Player.BSATN().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2360,7 +2360,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2405,7 +2405,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2450,7 +2450,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2494,7 +2494,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2543,7 +2543,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2594,7 +2594,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -2643,7 +2643,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) @@ -2688,7 +2688,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) @@ -2733,7 +2733,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) @@ -2780,7 +2780,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) @@ -3210,6 +3210,15 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { + // Module host calls are single-threaded in Wasm today, so the generated + // entrypoints reuse buffers across calls to avoid per-invocation allocation. + private static byte[] reducerArgsBuffer = new byte[0x10_000]; + private static byte[] procedureArgsBuffer = new byte[0x10_000]; + private static byte[] httpRequestBuffer = new byte[0x10_000]; + private static byte[] httpRequestBodyBuffer = new byte[0x10_000]; + private static byte[] viewArgsBuffer = new byte[0x10_000]; + private static byte[] anonymousViewArgsBuffer = new byte[0x10_000]; + sealed class __ReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( @@ -3225,7 +3234,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.__ReducerWithReservedPrefix((SpacetimeDB.ReducerContext)ctx); } @@ -3248,7 +3257,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { TestScheduleIssues.DummyScheduledReducer( (SpacetimeDB.ReducerContext)ctx, @@ -3272,7 +3281,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.OnReducerWithReservedPrefix((SpacetimeDB.ReducerContext)ctx); } @@ -3293,7 +3302,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.TestDuplicateReducerKind1((SpacetimeDB.ReducerContext)ctx); } @@ -3314,7 +3323,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.TestDuplicateReducerKind2((SpacetimeDB.ReducerContext)ctx); } @@ -3335,7 +3344,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.TestDuplicateReducerName((SpacetimeDB.ReducerContext)ctx); } @@ -3356,7 +3365,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.TestReducerReturnType((SpacetimeDB.ReducerContext)ctx); } @@ -3377,7 +3386,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { throw new System.InvalidOperationException(); } @@ -3395,10 +3404,7 @@ public static List ToListOrEmpty(T? value) [UnmanagedCallersOnly(EntryPoint = "__preinit__10_init_csharp")] #else // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer. - [DynamicDependency( - DynamicallyAccessedMemberTypes.PublicMethods, - typeof(SpacetimeDB.Internal.Module) - )] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ModuleRegistration))] #endif public static void Main() { @@ -3737,15 +3743,15 @@ public static void Main() } } - // Exports only work from the main assembly, so we need to generate forwarding methods. + // Export entrypoints live in generated module code so all build modes can + // dispatch directly to concrete generated functions. #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__describe_module__")] +#endif public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d); - [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")] - public static SpacetimeDB.Internal.Errno __call_reducer__( - int id, + private static SpacetimeDB.Internal.Errno __call_reducer_0( ulong sender_0, ulong sender_1, ulong sender_2, @@ -3755,123 +3761,35 @@ public static SpacetimeDB.Internal.Errno __call_reducer__( SpacetimeDB.Timestamp timestamp, SpacetimeDB.Internal.BytesSource args, SpacetimeDB.Internal.BytesSink error - ) => - id switch + ) + { + try { - 0 - => SpacetimeDB.Internal.Module.__call_reducer__<__ReducerWithReservedPrefix>( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 1 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 2 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 3 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 4 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 5 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 6 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - 7 - => SpacetimeDB.Internal.Module.__call_reducer__( - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ), - _ - => SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ) - }; + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + __ReducerWithReservedPrefix.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } - [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] - public static SpacetimeDB.Internal.Errno __call_procedure__( - int id, + private static SpacetimeDB.Internal.Errno __call_reducer_1( ulong sender_0, ulong sender_1, ulong sender_2, @@ -3880,180 +3798,920 @@ public static SpacetimeDB.Internal.Errno __call_procedure__( ulong conn_id_1, SpacetimeDB.Timestamp timestamp, SpacetimeDB.Internal.BytesSource args, - SpacetimeDB.Internal.BytesSink result_sink - ) => - id switch + SpacetimeDB.Internal.BytesSink error + ) + { + try { - _ - => SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ) - }; + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + DummyScheduledReducer.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } - [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] - public static SpacetimeDB.Internal.Errno __call_http_handler__( - int id, + private static SpacetimeDB.Internal.Errno __call_reducer_2( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, SpacetimeDB.Timestamp timestamp, - SpacetimeDB.Internal.BytesSource request, - SpacetimeDB.Internal.BytesSource request_body, - SpacetimeDB.Internal.BytesSink response_sink, - SpacetimeDB.Internal.BytesSink response_body_sink - ) => - id switch + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try { - _ - => SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ) - }; + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + OnReducerWithReservedPrefix.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } - [UnmanagedCallersOnly(EntryPoint = "__call_view__")] - public static SpacetimeDB.Internal.Errno __call_view__( - int id, + private static SpacetimeDB.Internal.Errno __call_reducer_3( ulong sender_0, ulong sender_1, ulong sender_2, ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, SpacetimeDB.Internal.BytesSource args, - SpacetimeDB.Internal.BytesSink sink - ) => - id switch + SpacetimeDB.Internal.BytesSink error + ) + { + try { - 0 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 1 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 2 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 3 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 4 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 5 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 6 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 7 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 8 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 9 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + TestDuplicateReducerKind1.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_4( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + TestDuplicateReducerKind2.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_5( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + TestDuplicateReducerName.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_6( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + TestReducerReturnType.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_7( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + TestReducerWithoutContext.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_view_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_ienumerable_return_from_filterViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_1( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_ienumerable_return_from_iterViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_2( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_no_contextViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_3( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_no_publicViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_4( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_wrong_contextViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_5( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_wrong_returnViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_6( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_no_deleteViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_7( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_no_insertViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_8( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_primary_key_missing_columnViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_9( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_primary_key_non_equatable_columnViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_10( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_primary_key_uses_non_bsatn_partial_fieldViewDispatcher.Invoke( + reader, + ctx + ); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_11( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_primary_key_uses_wrong_source_nameViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_anon_0( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref anonymousViewArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_index_no_mutationViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_anon_1( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref anonymousViewArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_no_anon_identityViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_anon_2( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref anonymousViewArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_no_iterViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_anon_3( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref anonymousViewArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = view_def_returns_not_a_spacetime_typeViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER + [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")] +#endif + public static SpacetimeDB.Internal.Errno __call_reducer__( + int id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) => + id switch + { + 0 + => __call_reducer_0( + sender_0, + sender_1, sender_2, sender_3, + conn_id_0, + conn_id_1, + timestamp, args, - sink + error ), - 10 - => SpacetimeDB.Internal.Module.__call_view__( + 1 + => __call_reducer_1( sender_0, sender_1, sender_2, sender_3, + conn_id_0, + conn_id_1, + timestamp, args, - sink + error ), - 11 - => SpacetimeDB.Internal.Module.__call_view__( + 2 + => __call_reducer_2( sender_0, sender_1, sender_2, sender_3, + conn_id_0, + conn_id_1, + timestamp, args, - sink + error ), - _ - => SpacetimeDB.Internal.Module.__call_view__( - id, + 3 + => __call_reducer_3( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 4 + => __call_reducer_4( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 5 + => __call_reducer_5( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 6 + => __call_reducer_6( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp, + args, + error + ), + 7 + => __call_reducer_7( sender_0, sender_1, sender_2, sender_3, + conn_id_0, + conn_id_1, + timestamp, args, - sink + error + ), + _ + => SpacetimeDB.Internal.Module.WriteReducerError( + error, + new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown reducer id") + ) + }; + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER + [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] +#endif + public static SpacetimeDB.Internal.Errno __call_procedure__( + int id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) => + id switch + { + _ + => throw new System.ArgumentOutOfRangeException( + nameof(id), + id, + "Unknown procedure id" + ) + }; + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER + [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] +#endif + public static SpacetimeDB.Internal.Errno __call_http_handler__( + int id, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource request, + SpacetimeDB.Internal.BytesSource request_body, + SpacetimeDB.Internal.BytesSink response_sink, + SpacetimeDB.Internal.BytesSink response_body_sink + ) => + id switch + { + _ + => throw new System.ArgumentOutOfRangeException( + nameof(id), + id, + "Unknown HTTP handler id" ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] +#endif + public static SpacetimeDB.Internal.Errno __call_view__( + int id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => + id switch + { + 0 => __call_view_0(sender_0, sender_1, sender_2, sender_3, args, sink), + 1 => __call_view_1(sender_0, sender_1, sender_2, sender_3, args, sink), + 2 => __call_view_2(sender_0, sender_1, sender_2, sender_3, args, sink), + 3 => __call_view_3(sender_0, sender_1, sender_2, sender_3, args, sink), + 4 => __call_view_4(sender_0, sender_1, sender_2, sender_3, args, sink), + 5 => __call_view_5(sender_0, sender_1, sender_2, sender_3, args, sink), + 6 => __call_view_6(sender_0, sender_1, sender_2, sender_3, args, sink), + 7 => __call_view_7(sender_0, sender_1, sender_2, sender_3, args, sink), + 8 => __call_view_8(sender_0, sender_1, sender_2, sender_3, args, sink), + 9 => __call_view_9(sender_0, sender_1, sender_2, sender_3, args, sink), + 10 => __call_view_10(sender_0, sender_1, sender_2, sender_3, args, sink), + 11 => __call_view_11(sender_0, sender_1, sender_2, sender_3, args, sink), + _ => UnknownViewId(id) + }; + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] +#endif public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, @@ -4061,29 +4719,24 @@ SpacetimeDB.Internal.BytesSink sink ) => id switch { - 0 - => SpacetimeDB.Internal.Module.__call_view_anon__( - args, - sink - ), - 1 - => SpacetimeDB.Internal.Module.__call_view_anon__( - args, - sink - ), - 2 - => SpacetimeDB.Internal.Module.__call_view_anon__( - args, - sink - ), - 3 - => SpacetimeDB.Internal.Module.__call_view_anon__( - args, - sink - ), - _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + 0 => __call_view_anon_0(args, sink), + 1 => __call_view_anon_1(args, sink), + 2 => __call_view_anon_2(args, sink), + 3 => __call_view_anon_3(args, sink), + _ => UnknownAnonymousViewId(id) }; -#endif + + private static SpacetimeDB.Internal.Errno UnknownViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + + private static SpacetimeDB.Internal.Errno UnknownAnonymousViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown anonymous view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } } #pragma warning restore STDB_UNSTABLE diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs index 3b5bcc02f65..015ec80b3ad 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs @@ -418,7 +418,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -511,6 +511,15 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { + // Module host calls are single-threaded in Wasm today, so the generated + // entrypoints reuse buffers across calls to avoid per-invocation allocation. + private static byte[] reducerArgsBuffer = new byte[0x10_000]; + private static byte[] procedureArgsBuffer = new byte[0x10_000]; + private static byte[] httpRequestBuffer = new byte[0x10_000]; + private static byte[] httpRequestBodyBuffer = new byte[0x10_000]; + private static byte[] viewArgsBuffer = new byte[0x10_000]; + private static byte[] anonymousViewArgsBuffer = new byte[0x10_000]; + sealed class DemoReducer : SpacetimeDB.Internal.IReducer { private static readonly SpacetimeDB.BSATN.I32 valueRW = new(); @@ -528,7 +537,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.DemoReducer((SpacetimeDB.ReducerContext)ctx, valueRW.Read(reader)); } @@ -546,7 +555,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable ); - public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) + public static byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { Reducers.DemoProcedure((SpacetimeDB.ProcedureContext)ctx); return System.Array.Empty(); @@ -565,10 +574,7 @@ public static List ToListOrEmpty(T? value) [UnmanagedCallersOnly(EntryPoint = "__preinit__10_init_csharp")] #else // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer. - [DynamicDependency( - DynamicallyAccessedMemberTypes.PublicMethods, - typeof(SpacetimeDB.Internal.Module) - )] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ModuleRegistration))] #endif public static void Main() { @@ -627,13 +633,125 @@ public static void Main() >(); } - // Exports only work from the main assembly, so we need to generate forwarding methods. + // Export entrypoints live in generated module code so all build modes can + // dispatch directly to concrete generated functions. #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__describe_module__")] +#endif public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d); + private static SpacetimeDB.Internal.Errno __call_reducer_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + DemoReducer.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_procedure_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateProcedureContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref procedureArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = DemoProcedure.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "procedure arguments"); + SpacetimeDB.Internal.Module.WriteBytes(result_sink, bytes); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking procedure: {e}"); + throw; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = demo_viewViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")] +#endif public static SpacetimeDB.Internal.Errno __call_reducer__( int id, ulong sender_0, @@ -649,7 +767,7 @@ SpacetimeDB.Internal.BytesSink error id switch { 0 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_0( sender_0, sender_1, sender_2, @@ -661,21 +779,15 @@ SpacetimeDB.Internal.BytesSink error error ), _ - => SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error + => SpacetimeDB.Internal.Module.WriteReducerError( + error, + new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown reducer id") ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] +#endif public static SpacetimeDB.Internal.Errno __call_procedure__( int id, ulong sender_0, @@ -691,7 +803,7 @@ SpacetimeDB.Internal.BytesSink result_sink id switch { 0 - => SpacetimeDB.Internal.Module.__call_procedure__( + => __call_procedure_0( sender_0, sender_1, sender_2, @@ -703,21 +815,16 @@ SpacetimeDB.Internal.BytesSink result_sink result_sink ), _ - => SpacetimeDB.Internal.Module.__call_procedure__( + => throw new System.ArgumentOutOfRangeException( + nameof(id), id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink + "Unknown procedure id" ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] +#endif public static SpacetimeDB.Internal.Errno __call_http_handler__( int id, SpacetimeDB.Timestamp timestamp, @@ -729,17 +836,16 @@ SpacetimeDB.Internal.BytesSink response_body_sink id switch { _ - => SpacetimeDB.Internal.Module.__call_http_handler__( + => throw new System.ArgumentOutOfRangeException( + nameof(id), id, - timestamp, - request, - request_body, - response_sink, - response_body_sink + "Unknown HTTP handler id" ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view__")] +#endif public static SpacetimeDB.Internal.Errno __call_view__( int id, ulong sender_0, @@ -751,28 +857,13 @@ SpacetimeDB.Internal.BytesSink sink ) => id switch { - 0 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - _ - => SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ) + 0 => __call_view_0(sender_0, sender_1, sender_2, sender_3, args, sink), + _ => UnknownViewId(id) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] +#endif public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, @@ -780,9 +871,20 @@ SpacetimeDB.Internal.BytesSink sink ) => id switch { - _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + _ => UnknownAnonymousViewId(id) }; -#endif + + private static SpacetimeDB.Internal.Errno UnknownViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + + private static SpacetimeDB.Internal.Errno UnknownAnonymousViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown anonymous view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } } #pragma warning restore STDB_UNSTABLE diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index 3378de69b21..82e3d7bdecf 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -1773,7 +1773,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar ) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -1815,7 +1815,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IViewContext ctx ) @@ -1862,7 +1862,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar >().GetAlgebraicType(registrar) ); - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) @@ -2322,6 +2322,15 @@ public sealed partial class LocalReadOnly static class ModuleRegistration { + // Module host calls are single-threaded in Wasm today, so the generated + // entrypoints reuse buffers across calls to avoid per-invocation allocation. + private static byte[] reducerArgsBuffer = new byte[0x10_000]; + private static byte[] procedureArgsBuffer = new byte[0x10_000]; + private static byte[] httpRequestBuffer = new byte[0x10_000]; + private static byte[] httpRequestBodyBuffer = new byte[0x10_000]; + private static byte[] viewArgsBuffer = new byte[0x10_000]; + private static byte[] anonymousViewArgsBuffer = new byte[0x10_000]; + sealed class Init : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef( @@ -2337,7 +2346,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Timers.Init((SpacetimeDB.ReducerContext)ctx); } @@ -2360,7 +2369,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.InsertData((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader)); } @@ -2383,7 +2392,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Test.NestingNamespaces.AndClasses.InsertData2( (SpacetimeDB.ReducerContext)ctx, @@ -2409,7 +2418,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { MultiTableRow.InsertMultiData((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader)); } @@ -2432,7 +2441,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Reducers.ScheduleImmediate((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader)); } @@ -2455,7 +2464,7 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public SpacetimeDB.Internal.Lifecycle? Lifecycle => null; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { Timers.SendScheduledMessage((SpacetimeDB.ReducerContext)ctx, argRW.Read(reader)); } @@ -2473,10 +2482,7 @@ public static List ToListOrEmpty(T? value) [UnmanagedCallersOnly(EntryPoint = "__preinit__10_init_csharp")] #else // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer. - [DynamicDependency( - DynamicallyAccessedMemberTypes.PublicMethods, - typeof(SpacetimeDB.Internal.Module) - )] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ModuleRegistration))] #endif public static void Main() { @@ -2555,13 +2561,329 @@ public static void Main() ); } - // Exports only work from the main assembly, so we need to generate forwarding methods. + // Export entrypoints live in generated module code so all build modes can + // dispatch directly to concrete generated functions. #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__describe_module__")] +#endif public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d); + private static SpacetimeDB.Internal.Errno __call_reducer_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + Init.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_1( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + InsertData.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_2( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + InsertData2.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_3( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + InsertMultiData.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_4( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + ScheduleImmediate.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_reducer_5( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext( + sender_0, + sender_1, + sender_2, + sender_3, + conn_id_0, + conn_id_1, + timestamp + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref reducerArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + SendScheduledMessage.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } + catch (System.Exception e) + { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + + private static SpacetimeDB.Internal.Errno __call_view_0( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = public_table_queryViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_1( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext( + sender_0, + sender_1, + sender_2, + sender_3 + ); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = public_table_viewViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + + private static SpacetimeDB.Internal.Errno __call_view_anon_0( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) + { + try + { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes( + args, + ref anonymousViewArgsBuffer + ); + using var reader = new System.IO.BinaryReader(stream); + var bytes = find_public_table__by_identityViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } + catch (System.Exception e) + { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")] +#endif public static SpacetimeDB.Internal.Errno __call_reducer__( int id, ulong sender_0, @@ -2577,7 +2899,7 @@ SpacetimeDB.Internal.BytesSink error id switch { 0 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_0( sender_0, sender_1, sender_2, @@ -2589,7 +2911,7 @@ SpacetimeDB.Internal.BytesSink error error ), 1 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_1( sender_0, sender_1, sender_2, @@ -2601,7 +2923,7 @@ SpacetimeDB.Internal.BytesSink error error ), 2 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_2( sender_0, sender_1, sender_2, @@ -2613,7 +2935,7 @@ SpacetimeDB.Internal.BytesSink error error ), 3 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_3( sender_0, sender_1, sender_2, @@ -2625,7 +2947,7 @@ SpacetimeDB.Internal.BytesSink error error ), 4 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_4( sender_0, sender_1, sender_2, @@ -2637,7 +2959,7 @@ SpacetimeDB.Internal.BytesSink error error ), 5 - => SpacetimeDB.Internal.Module.__call_reducer__( + => __call_reducer_5( sender_0, sender_1, sender_2, @@ -2649,21 +2971,15 @@ SpacetimeDB.Internal.BytesSink error error ), _ - => SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error + => SpacetimeDB.Internal.Module.WriteReducerError( + error, + new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown reducer id") ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] +#endif public static SpacetimeDB.Internal.Errno __call_procedure__( int id, ulong sender_0, @@ -2679,21 +2995,16 @@ SpacetimeDB.Internal.BytesSink result_sink id switch { _ - => SpacetimeDB.Internal.Module.__call_procedure__( + => throw new System.ArgumentOutOfRangeException( + nameof(id), id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink + "Unknown procedure id" ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] +#endif public static SpacetimeDB.Internal.Errno __call_http_handler__( int id, SpacetimeDB.Timestamp timestamp, @@ -2705,17 +3016,16 @@ SpacetimeDB.Internal.BytesSink response_body_sink id switch { _ - => SpacetimeDB.Internal.Module.__call_http_handler__( + => throw new System.ArgumentOutOfRangeException( + nameof(id), id, - timestamp, - request, - request_body, - response_sink, - response_body_sink + "Unknown HTTP handler id" ) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view__")] +#endif public static SpacetimeDB.Internal.Errno __call_view__( int id, ulong sender_0, @@ -2727,37 +3037,14 @@ SpacetimeDB.Internal.BytesSink sink ) => id switch { - 0 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - 1 - => SpacetimeDB.Internal.Module.__call_view__( - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ), - _ - => SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ) + 0 => __call_view_0(sender_0, sender_1, sender_2, sender_3, args, sink), + 1 => __call_view_1(sender_0, sender_1, sender_2, sender_3, args, sink), + _ => UnknownViewId(id) }; +#if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] +#endif public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, @@ -2765,14 +3052,21 @@ SpacetimeDB.Internal.BytesSink sink ) => id switch { - 0 - => SpacetimeDB.Internal.Module.__call_view_anon__( - args, - sink - ), - _ => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink) + 0 => __call_view_anon_0(args, sink), + _ => UnknownAnonymousViewId(id) }; -#endif + + private static SpacetimeDB.Internal.Errno UnknownViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + + private static SpacetimeDB.Internal.Errno UnknownAnonymousViewId(int id) + { + SpacetimeDB.Log.Error($"Unknown anonymous view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } } #pragma warning restore STDB_UNSTABLE diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 5634ad21219..aad1bf54c02 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1481,7 +1481,7 @@ sealed class {{{Name}}}ViewDispatcher : {{{interfaceName}}} { public SpacetimeDB.Internal.RawViewDefV10 {{{makeViewDefMethod}}}(SpacetimeDB.BSATN.ITypeRegistrar registrar) => {{{GenerateViewDef(index)}}} - public byte[] Invoke( + public static byte[] Invoke( System.IO.BinaryReader reader, {{{interfaceContext}}} ctx ) { @@ -1586,7 +1586,7 @@ sealed class {{Identifier}}: SpacetimeDB.Internal.IReducer { _ => "null" }}}; - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { + public static void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) { {{invocation}}; } } @@ -1807,7 +1807,7 @@ sealed class {{{Identifier}}} : SpacetimeDB.Internal.IProcedure { Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable ); - public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { + public static byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) { {{{paramReads}}}{{{invokeBody}}} } } @@ -1944,7 +1944,7 @@ sealed class {{Identifier}} : SpacetimeDB.Internal.IHttpHandler { SourceName: nameof({{Identifier}}) ); - public SpacetimeDB.HttpResponse Invoke( + public static SpacetimeDB.HttpResponse Invoke( SpacetimeDB.HandlerContextBase ctx, SpacetimeDB.HttpRequest request ) { @@ -2797,6 +2797,15 @@ public sealed partial class LocalReadOnly { } static class ModuleRegistration { + // Module host calls are single-threaded in Wasm today, so the generated + // entrypoints reuse buffers across calls to avoid per-invocation allocation. + private static byte[] reducerArgsBuffer = new byte[0x10_000]; + private static byte[] procedureArgsBuffer = new byte[0x10_000]; + private static byte[] httpRequestBuffer = new byte[0x10_000]; + private static byte[] httpRequestBodyBuffer = new byte[0x10_000]; + private static byte[] viewArgsBuffer = new byte[0x10_000]; + private static byte[] anonymousViewArgsBuffer = new byte[0x10_000]; + {{string.Join("\n", addReducers.Select(r => r.Class))}} {{string.Join("\n", addProcedures.Select(r => r.Class))}} @@ -2815,7 +2824,7 @@ public static List ToListOrEmpty(T? value) where T : class [UnmanagedCallersOnly(EntryPoint = "__preinit__10_init_csharp")] #else // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer. - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(SpacetimeDB.Internal.Module))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(ModuleRegistration))] #endif public static void Main() { SpacetimeDB.Internal.Module.SetReducerContextConstructor((identity, connectionId, random, time) => new SpacetimeDB.ReducerContext(identity, connectionId, random, time)); @@ -2889,12 +2898,158 @@ public static void Main() { )}} } - // Exports only work from the main assembly, so we need to generate forwarding methods. + // Export entrypoints live in generated module code so all build modes can + // dispatch directly to concrete generated functions. #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__describe_module__")] + #endif public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d); + {{string.Join( + "\n\n", + addReducers.Select((r, i) => + $$""" + private static SpacetimeDB.Internal.Errno __call_reducer_{{i}}( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink error + ) { + try { + var ctx = SpacetimeDB.Internal.Module.CreateReducerContext(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref reducerArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + {{EscapeIdentifier(r.Name)}}.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "reducer arguments"); + return SpacetimeDB.Internal.Errno.OK; + } catch (System.Exception e) { + return SpacetimeDB.Internal.Module.WriteReducerError(error, e); + } + } + """ + ) + )}} + + {{string.Join( + "\n\n", + addProcedures.Select((p, i) => + $$""" + private static SpacetimeDB.Internal.Errno __call_procedure_{{i}}( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + ulong conn_id_0, + ulong conn_id_1, + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink result_sink + ) { + try { + var ctx = SpacetimeDB.Internal.Module.CreateProcedureContext(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref procedureArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = {{EscapeIdentifier(p.Name)}}.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.EnsureNoUnreadBytes(stream, "procedure arguments"); + SpacetimeDB.Internal.Module.WriteBytes(result_sink, bytes); + return SpacetimeDB.Internal.Errno.OK; + } catch (System.Exception e) { + SpacetimeDB.Log.Error($"Error while invoking procedure: {e}"); + throw; + } + } + """ + ) + )}} + + {{string.Join( + "\n\n", + addHttpHandlers.Select((h, i) => + $$""" + private static SpacetimeDB.Internal.Errno __call_http_handler_{{i}}( + SpacetimeDB.Timestamp timestamp, + SpacetimeDB.Internal.BytesSource request, + SpacetimeDB.Internal.BytesSource request_body, + SpacetimeDB.Internal.BytesSink response_sink, + SpacetimeDB.Internal.BytesSink response_body_sink + ) { + try { + var ctx = SpacetimeDB.Internal.Module.CreateHandlerContext(timestamp); + var response = {{EscapeIdentifier(h.Name)}}.Invoke( + ctx, + SpacetimeDB.Internal.Module.ReadHttpRequest(request, ref httpRequestBuffer, request_body, ref httpRequestBodyBuffer) + ); + SpacetimeDB.Internal.Module.WriteHttpResponse(response_sink, response_body_sink, response); + return SpacetimeDB.Internal.Errno.OK; + } catch (System.Exception e) { + SpacetimeDB.Log.Error($"Error while invoking HTTP handler: {e}"); + throw; + } + } + """ + ) + )}} + + {{string.Join( + "\n\n", + views.Array.Where(v => !v.IsAnonymous).Select((v, i) => + $$""" + private static SpacetimeDB.Internal.Errno __call_view_{{i}}( + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) { + try { + var ctx = SpacetimeDB.Internal.Module.CreateViewContext(sender_0, sender_1, sender_2, sender_3); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref viewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = {{v.Name}}ViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } catch (System.Exception e) { + SpacetimeDB.Log.Error($"Error while invoking view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + """ + ) + )}} + + {{string.Join( + "\n\n", + views.Array.Where(v => v.IsAnonymous).Select((v, i) => + $$""" + private static SpacetimeDB.Internal.Errno __call_view_anon_{{i}}( + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) { + try { + var ctx = SpacetimeDB.Internal.Module.CreateAnonymousViewContext(); + using var stream = SpacetimeDB.Internal.Module.ConsumeBytes(args, ref anonymousViewArgsBuffer); + using var reader = new System.IO.BinaryReader(stream); + var bytes = {{v.Name}}ViewDispatcher.Invoke(reader, ctx); + SpacetimeDB.Internal.Module.WriteBytes(sink, bytes); + return (SpacetimeDB.Internal.Errno)2; + } catch (System.Exception e) { + SpacetimeDB.Log.Error($"Error while invoking anonymous view: {e}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + } + """ + ) + )}} + + #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")] + #endif public static SpacetimeDB.Internal.Errno __call_reducer__( int id, ulong sender_0, @@ -2910,24 +3065,15 @@ SpacetimeDB.Internal.BytesSink error {{string.Join( "\n", addReducers.Select((r, i) => - $"{i} => SpacetimeDB.Internal.Module.__call_reducer__<{EscapeIdentifier(r.Name)}>(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, error)," + $"{i} => __call_reducer_{i}(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, error)," ) )}} - _ => SpacetimeDB.Internal.Module.__call_reducer__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - error - ) + _ => SpacetimeDB.Internal.Module.WriteReducerError(error, new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown reducer id")) }; + #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_procedure__")] + #endif public static SpacetimeDB.Internal.Errno __call_procedure__( int id, ulong sender_0, @@ -2943,24 +3089,15 @@ SpacetimeDB.Internal.BytesSink result_sink {{string.Join( "\n", addProcedures.Select((p, i) => - $"{i} => SpacetimeDB.Internal.Module.__call_procedure__<{EscapeIdentifier(p.Name)}>(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, result_sink)," + $"{i} => __call_procedure_{i}(sender_0, sender_1, sender_2, sender_3, conn_id_0, conn_id_1, timestamp, args, result_sink)," ) )}} - _ => SpacetimeDB.Internal.Module.__call_procedure__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - conn_id_0, - conn_id_1, - timestamp, - args, - result_sink - ) + _ => throw new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown procedure id") }; + #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_http_handler__")] + #endif public static SpacetimeDB.Internal.Errno __call_http_handler__( int id, SpacetimeDB.Timestamp timestamp, @@ -2972,20 +3109,15 @@ SpacetimeDB.Internal.BytesSink response_body_sink {{string.Join( "\n", addHttpHandlers.Select((h, i) => - $"{i} => SpacetimeDB.Internal.Module.__call_http_handler__<{EscapeIdentifier(h.Name)}>(timestamp, request, request_body, response_sink, response_body_sink)," + $"{i} => __call_http_handler_{i}(timestamp, request, request_body, response_sink, response_body_sink)," ) )}} - _ => SpacetimeDB.Internal.Module.__call_http_handler__( - id, - timestamp, - request, - request_body, - response_sink, - response_body_sink - ) + _ => throw new System.ArgumentOutOfRangeException(nameof(id), id, "Unknown HTTP handler id") }; + #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view__")] + #endif public static SpacetimeDB.Internal.Errno __call_view__( int id, ulong sender_0, @@ -2998,21 +3130,15 @@ SpacetimeDB.Internal.BytesSink sink {{string.Join("\n", views.Array.Where(v => !v.IsAnonymous) .Select((v, i) => - $"{i} => SpacetimeDB.Internal.Module.__call_view__<{v.Name}ViewDispatcher>(sender_0, sender_1, sender_2, sender_3, args, sink)," + $"{i} => __call_view_{i}(sender_0, sender_1, sender_2, sender_3, args, sink)," ) )}} - _ => SpacetimeDB.Internal.Module.__call_view__( - id, - sender_0, - sender_1, - sender_2, - sender_3, - args, - sink - ) + _ => UnknownViewId(id) }; + #if EXPERIMENTAL_WASM_AOT || NET10_0_OR_GREATER [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] + #endif public static SpacetimeDB.Internal.Errno __call_view_anon__( int id, SpacetimeDB.Internal.BytesSource args, @@ -3021,16 +3147,21 @@ SpacetimeDB.Internal.BytesSink sink {{string.Join("\n", views.Array.Where(v => v.IsAnonymous) .Select((v, i) => - $"{i} => SpacetimeDB.Internal.Module.__call_view_anon__<{v.Name}ViewDispatcher>(args, sink)," + $"{i} => __call_view_anon_{i}(args, sink)," ) )}} - _ => SpacetimeDB.Internal.Module.__call_view_anon__( - id, - args, - sink - ) + _ => UnknownAnonymousViewId(id) }; - #endif + + private static SpacetimeDB.Internal.Errno UnknownViewId(int id) { + SpacetimeDB.Log.Error($"Unknown view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } + + private static SpacetimeDB.Internal.Errno UnknownAnonymousViewId(int id) { + SpacetimeDB.Log.Error($"Unknown anonymous view id: {id}"); + return SpacetimeDB.Internal.Errno.HOST_CALL_FAILURE; + } } #pragma warning restore STDB_UNSTABLE diff --git a/crates/bindings-csharp/Runtime/Internal/IHttpHandler.cs b/crates/bindings-csharp/Runtime/Internal/IHttpHandler.cs index 7c920d1bd90..74e3930fedf 100644 --- a/crates/bindings-csharp/Runtime/Internal/IHttpHandler.cs +++ b/crates/bindings-csharp/Runtime/Internal/IHttpHandler.cs @@ -3,9 +3,4 @@ namespace SpacetimeDB.Internal; public interface IHttpHandler { RawHttpHandlerDefV10 MakeHandlerDef(); - - SpacetimeDB.HttpResponse Invoke( - SpacetimeDB.HandlerContextBase ctx, - SpacetimeDB.HttpRequest request - ); } diff --git a/crates/bindings-csharp/Runtime/Internal/IReducer.cs b/crates/bindings-csharp/Runtime/Internal/IReducer.cs index d51b5a03206..3e3946da854 100644 --- a/crates/bindings-csharp/Runtime/Internal/IReducer.cs +++ b/crates/bindings-csharp/Runtime/Internal/IReducer.cs @@ -22,9 +22,6 @@ public interface IReducer Lifecycle? Lifecycle { get; } - // This one is not static because we need to be able to store IReducer in a list. - void Invoke(BinaryReader reader, IReducerContext args); - public static void VolatileNonatomicScheduleImmediate(string name, MemoryStream args) { var name_bytes = Encoding.UTF8.GetBytes(name); diff --git a/crates/bindings-csharp/Runtime/Internal/IView.cs b/crates/bindings-csharp/Runtime/Internal/IView.cs index 0b29f233783..5a2fc4b3839 100644 --- a/crates/bindings-csharp/Runtime/Internal/IView.cs +++ b/crates/bindings-csharp/Runtime/Internal/IView.cs @@ -5,17 +5,11 @@ namespace SpacetimeDB.Internal; public interface IView { RawViewDefV10 MakeViewDef(ITypeRegistrar registrar); - - // This one is not static because we need to be able to store IView in a list. - byte[] Invoke(BinaryReader reader, IViewContext args); } public interface IAnonymousView { RawViewDefV10 MakeAnonymousViewDef(ITypeRegistrar registrar); - - // This one is not static because we need to be able to store IAnonymousView in a list. - byte[] Invoke(BinaryReader reader, IAnonymousViewContext args); } public interface IViewContext diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index bcebf97d59e..4f72b8be109 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -70,6 +70,9 @@ internal void RegisterReducer(RawReducerDefV10 reducer, Lifecycle? lifecycle) internal void RegisterHttpHandler(RawHttpHandlerDefV10 handler) => httpHandlerDefs.Add(handler); + internal bool HasHttpHandler(string sourceName) => + httpHandlerDefs.Any(handler => handler.SourceName == sourceName); + internal void RegisterHttpRoute(RawHttpRouteDefV10 route) => httpRouteDefs.Add(route); internal void RegisterTable(RawTableDefV10 table, RawScheduleDefV10? schedule) @@ -259,12 +262,6 @@ private static void EnsureNativeAotTypeRoots() private static readonly RawModuleDefV10 moduleDef = new(); - private static readonly List reducers = []; - private static readonly List procedures = []; - private static readonly List httpHandlers = []; - private static readonly List viewDispatchers = []; - private static readonly List anonymousViewDispatchers = []; - private static class ReducerCache where R : IReducer, new() { @@ -368,7 +365,6 @@ public static void RegisterReducer() where R : IReducer, new() { var reducer = ReducerCache.Instance; - reducers.Add(reducer); moduleDef.RegisterReducer(reducer.MakeReducerDef(typeRegistrar), reducer.Lifecycle); } @@ -376,7 +372,6 @@ public static void RegisterProcedure

() where P : IProcedure, new() { var procedure = ProcedureCache

.Instance; - procedures.Add(procedure); moduleDef.RegisterProcedure(procedure.MakeProcedureDef(typeRegistrar)); } @@ -384,7 +379,6 @@ public static void RegisterHttpHandler() where H : IHttpHandler, new() { var handler = HttpHandlerCache.Instance; - httpHandlers.Add(handler); moduleDef.RegisterHttpHandler(handler.MakeHandlerDef()); } @@ -393,9 +387,7 @@ public static void RegisterHttpRouter(SpacetimeDB.Router router) foreach (var route in router.GetRoutes()) { if ( - !httpHandlers.Any(handler => - handler.MakeHandlerDef().SourceName == route.HandlerFunction - ) + !moduleDef.HasHttpHandler(route.HandlerFunction) ) { throw new ArgumentException( @@ -426,7 +418,6 @@ public static void RegisterView() { var dispatcher = ViewDispatcherCache.Instance; var def = dispatcher.MakeViewDef(typeRegistrar); - viewDispatchers.Add(dispatcher); moduleDef.RegisterView(def); } @@ -435,7 +426,6 @@ public static void RegisterAnonymousView() { var dispatcher = AnonymousViewDispatcherCache.Instance; var def = dispatcher.MakeAnonymousViewDef(typeRegistrar); - anonymousViewDispatchers.Add(dispatcher); moduleDef.RegisterView(def); } @@ -531,436 +521,136 @@ internal static MemoryStream Consume(this BytesSource source, ref byte[] buffer) } } - private static void Write(this BytesSink sink, ReadOnlySpan bytes) - { - while (!bytes.IsEmpty) - { - var written = bytes.Length; - FFI.bytes_sink_write(sink, bytes, ref written); - bytes = bytes[written..]; - } - } - - // __call_reducer__ is not invoked in parallel because modules do not support multithreading in Wasm. - private static byte[] reducerArgsBuffer = new byte[0x10_000]; - private static byte[] procedureArgsBuffer = new byte[0x10_000]; - private static byte[] httpRequestBuffer = new byte[0x10_000]; - private static byte[] httpRequestBodyBuffer = new byte[0x10_000]; - private static byte[] viewArgsBuffer = new byte[0x10_000]; - private static byte[] anonymousViewArgsBuffer = new byte[0x10_000]; + public static void WriteBytes(BytesSink sink, ReadOnlySpan bytes) => sink.Write(bytes); -#pragma warning disable IDE1006 // Naming Styles - methods below are meant for FFI. + public static MemoryStream ConsumeBytes(BytesSource source, ref byte[] buffer) => + source.Consume(ref buffer); - public static void __describe_module__(BytesSink description) + public static SpacetimeDB.HttpRequest ReadHttpRequest( + BytesSource request, + ref byte[] requestBuffer, + BytesSource requestBody, + ref byte[] requestBodyBuffer + ) { - EnsureNativeAotTypeRoots(); - try - { - var module = moduleDef.BuildModuleDefinition(); - RawModuleDef versioned = new RawModuleDef.V10(module); - var moduleBytes = IStructuralReadWrite.ToBytes(new RawModuleDef.BSATN(), versioned); - description.Write(moduleBytes); - } - catch (Exception e) - { - Log.Error($"Error while describing the module: {e}"); - } + using var stream = ConsumeBytes(request, ref requestBuffer); + using var reader = new BinaryReader(stream); + var requestWire = new HttpRequestWire.BSATN().Read(reader); + EnsureNoUnreadBytes(stream, "HTTP handler request"); + + using var requestBodyStream = ConsumeBytes(requestBody, ref requestBodyBuffer); + return SpacetimeDB.HttpClient.FromWire(requestWire, requestBodyStream.ToArray()); } - public static Errno __call_reducer__( - int id, - ulong sender_0, - ulong sender_1, - ulong sender_2, - ulong sender_3, - ulong conn_id_0, - ulong conn_id_1, - Timestamp timestamp, - BytesSource args, - BytesSink error + public static void WriteHttpResponse( + BytesSink responseSink, + BytesSink responseBodySink, + SpacetimeDB.HttpResponse response ) { - try - { - var senderIdentity = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - - var ctx = newReducerContext!(senderIdentity, connectionId, random, time); - - using var stream = args.Consume(ref reducerArgsBuffer); - using var reader = new BinaryReader(stream); - reducers[id].Invoke(reader, ctx); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the reducer arguments"); - } - return Errno.OK; /* no exception */ - } - catch (Exception e) - { - var error_str = e.Message ?? e.GetType().FullName ?? e.GetType().Name; - var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str); - error.Write(error_bytes); - return Errno.HOST_CALL_FAILURE; - } + var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); + responseSink.Write(IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire)); + responseBodySink.Write(responseBody); } - - public static Errno __call_reducer__( + public static IReducerContext CreateReducerContext( ulong sender_0, ulong sender_1, ulong sender_2, ulong sender_3, ulong conn_id_0, ulong conn_id_1, - Timestamp timestamp, - BytesSource args, - BytesSink error + Timestamp timestamp ) - where R : IReducer, new() { - try - { - var senderIdentity = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - - var ctx = newReducerContext!(senderIdentity, connectionId, random, time); + var senderIdentity = Identity.From( + MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) + ); + var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); - using var stream = args.Consume(ref reducerArgsBuffer); - using var reader = new BinaryReader(stream); - ReducerCache.Instance.Invoke(reader, ctx); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the reducer arguments"); - } - return Errno.OK; /* no exception */ - } - catch (Exception e) - { - var error_str = e.Message ?? e.GetType().FullName ?? e.GetType().Name; - var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str); - error.Write(error_bytes); - return Errno.HOST_CALL_FAILURE; - } + return newReducerContext!(senderIdentity, connectionId, random, time); } - public static Errno __call_procedure__( - int id, + public static IProcedureContext CreateProcedureContext( ulong sender_0, ulong sender_1, ulong sender_2, ulong sender_3, ulong conn_id_0, ulong conn_id_1, - Timestamp timestamp, - BytesSource args, - BytesSink resultSink + Timestamp timestamp ) { - try - { - var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - - var ctx = newProcedureContext!(sender, connectionId, random, time); + var sender = Identity.From(MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3])); + var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); - using var stream = args.Consume(ref procedureArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = procedures[id].Invoke(reader, ctx); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the procedure arguments"); - } - resultSink.Write(bytes); + return newProcedureContext!(sender, connectionId, random, time); + } - return Errno.OK; - } - catch (Exception e) - { - // Host contract __call_procedure__ must either return Errno.OK or trap. - // Returning other errno values here can put the host/runtime in an unexpected state, - // so we log and rethrow to trap on any exception. - Log.Error($"Error while invoking procedure: {e}"); - throw; - } + public static SpacetimeDB.HandlerContextBase CreateHandlerContext(Timestamp timestamp) + { + var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); + var time = timestamp.ToStd(); + return newHandlerContext!(random, time); } - public static Errno __call_procedure__

( + public static IViewContext CreateViewContext( ulong sender_0, ulong sender_1, ulong sender_2, - ulong sender_3, - ulong conn_id_0, - ulong conn_id_1, - Timestamp timestamp, - BytesSource args, - BytesSink resultSink + ulong sender_3 ) - where P : IProcedure, new() { - try - { - var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var connectionId = ConnectionId.From(MemoryMarshal.AsBytes([conn_id_0, conn_id_1])); - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - - var ctx = newProcedureContext!(sender, connectionId, random, time); - - using var stream = args.Consume(ref procedureArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = ProcedureCache

.Instance.Invoke(reader, ctx); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the procedure arguments"); - } - resultSink.Write(bytes); - - return Errno.OK; - } - catch (Exception e) - { - Log.Error($"Error while invoking procedure: {e}"); - throw; - } + var sender = Identity.From(MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3])); + return newViewContext!(sender); } - public static Errno __call_http_handler__( - int id, - Timestamp timestamp, - BytesSource request, - BytesSource requestBody, - BytesSink responseSink, - BytesSink responseBodySink - ) - { - try - { - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - var ctx = newHandlerContext!(random, time); - - using var stream = request.Consume(ref httpRequestBuffer); - using var reader = new BinaryReader(stream); - var requestWire = new HttpRequestWire.BSATN().Read(reader); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the HTTP handler request"); - } + public static IAnonymousViewContext CreateAnonymousViewContext() => newAnonymousViewContext!(); - using var requestBodyStream = requestBody.Consume(ref httpRequestBodyBuffer); - var response = httpHandlers[id] - .Invoke( - ctx, - SpacetimeDB.HttpClient.FromWire(requestWire, requestBodyStream.ToArray()) - ); - var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); - responseSink.Write( - IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire) - ); - responseBodySink.Write(responseBody); - - return Errno.OK; - } - catch (Exception e) + public static void EnsureNoUnreadBytes(MemoryStream stream, string description) + { + if (stream.Position != stream.Length) { - Log.Error($"Error while invoking HTTP handler: {e}"); - throw; + throw new Exception($"Unrecognised extra bytes in the {description}"); } } - public static Errno __call_http_handler__( - Timestamp timestamp, - BytesSource request, - BytesSource requestBody, - BytesSink responseSink, - BytesSink responseBodySink - ) - where H : IHttpHandler, new() - { - try - { - var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch); - var time = timestamp.ToStd(); - var ctx = newHandlerContext!(random, time); - - using var stream = request.Consume(ref httpRequestBuffer); - using var reader = new BinaryReader(stream); - var requestWire = new HttpRequestWire.BSATN().Read(reader); - if (stream.Position != stream.Length) - { - throw new Exception("Unrecognised extra bytes in the HTTP handler request"); - } - - using var requestBodyStream = requestBody.Consume(ref httpRequestBodyBuffer); - var response = HttpHandlerCache.Instance.Invoke( - ctx, - SpacetimeDB.HttpClient.FromWire(requestWire, requestBodyStream.ToArray()) - ); - var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); - responseSink.Write( - IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire) - ); - responseBodySink.Write(responseBody); - - return Errno.OK; - } - catch (Exception e) - { - Log.Error($"Error while invoking HTTP handler: {e}"); - throw; - } - } - - ///

- /// Called by the host to execute a view when the sender calls the view identified by . - /// - /// - /// - /// The sender identity is passed as 4 values ( through - /// ) representing a little-endian . - /// - /// - /// is a host-registered containing the BSATN-encoded - /// view arguments. For empty arguments, will be invalid. - /// - /// - /// The view output is written to , a host-registered . - /// - /// - /// Note: a previous view ABI wrote the return rows directly to the sink. - /// The current ABI writes a BSATN-encoded first, in order to distinguish - /// between views that return row data and views that return queries. - /// - /// - /// The current ABI is identified by returning error code 2. - /// - /// - public static Errno __call_view__( - int id, - ulong sender_0, - ulong sender_1, - ulong sender_2, - ulong sender_3, - BytesSource args, - BytesSink rows - ) + public static Errno WriteReducerError(BytesSink error, Exception e) { - try - { - var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var ctx = newViewContext!(sender); - using var stream = args.Consume(ref viewArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = viewDispatchers[id].Invoke(reader, ctx); - rows.Write(bytes); - return (Errno)2; - } - catch (Exception e) - { - Log.Error($"Error while invoking view: {e}"); - return Errno.HOST_CALL_FAILURE; - } + var error_str = e.Message ?? e.GetType().FullName ?? e.GetType().Name; + var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str); + error.Write(error_bytes); + return Errno.HOST_CALL_FAILURE; } - public static Errno __call_view__( - ulong sender_0, - ulong sender_1, - ulong sender_2, - ulong sender_3, - BytesSource args, - BytesSink rows - ) - where TDispatcher : IView, new() - { - try - { - var sender = Identity.From( - MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]) - ); - var ctx = newViewContext!(sender); - using var stream = args.Consume(ref viewArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = ViewDispatcherCache.Instance.Invoke(reader, ctx); - rows.Write(bytes); - return (Errno)2; - } - catch (Exception e) - { - Log.Error($"Error while invoking view: {e}"); - return Errno.HOST_CALL_FAILURE; - } - } - - /// - /// Called by the host to execute an anonymous view. - /// - /// - /// - /// is a host-registered containing the BSATN-encoded - /// view arguments. For empty arguments, will be invalid. - /// - /// - /// The view output is written to , a host-registered . - /// - /// - /// Note: a previous view ABI wrote the return rows directly to the sink. - /// The current ABI writes a BSATN-encoded first, in order to distinguish - /// between views that return row data and views that return queries. - /// - /// - /// The current ABI is identified by returning error code 2. - /// - /// - public static Errno __call_view_anon__(int id, BytesSource args, BytesSink rows) + private static void Write(this BytesSink sink, ReadOnlySpan bytes) { - try - { - var ctx = newAnonymousViewContext!(); - using var stream = args.Consume(ref anonymousViewArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = anonymousViewDispatchers[id].Invoke(reader, ctx); - rows.Write(bytes); - return (Errno)2; - } - catch (Exception e) + while (!bytes.IsEmpty) { - Log.Error($"Error while invoking anonymous view: {e}"); - return Errno.HOST_CALL_FAILURE; + var written = bytes.Length; + FFI.bytes_sink_write(sink, bytes, ref written); + bytes = bytes[written..]; } } - public static Errno __call_view_anon__(BytesSource args, BytesSink rows) - where TDispatcher : IAnonymousView, new() +#pragma warning disable IDE1006 // Naming Styles - methods below are meant for FFI. + + public static void __describe_module__(BytesSink description) { + EnsureNativeAotTypeRoots(); try { - var ctx = newAnonymousViewContext!(); - using var stream = args.Consume(ref anonymousViewArgsBuffer); - using var reader = new BinaryReader(stream); - var bytes = AnonymousViewDispatcherCache.Instance.Invoke(reader, ctx); - rows.Write(bytes); - return (Errno)2; + var module = moduleDef.BuildModuleDefinition(); + RawModuleDef versioned = new RawModuleDef.V10(module); + var moduleBytes = IStructuralReadWrite.ToBytes(new RawModuleDef.BSATN(), versioned); + description.Write(moduleBytes); } catch (Exception e) { - Log.Error($"Error while invoking anonymous view: {e}"); - return Errno.HOST_CALL_FAILURE; + Log.Error($"Error while describing the module: {e}"); } } } diff --git a/crates/bindings-csharp/Runtime/Internal/Procedure.cs b/crates/bindings-csharp/Runtime/Internal/Procedure.cs index becd184d13b..247cef613d5 100644 --- a/crates/bindings-csharp/Runtime/Internal/Procedure.cs +++ b/crates/bindings-csharp/Runtime/Internal/Procedure.cs @@ -14,11 +14,6 @@ public interface IProcedure /// Creates a procedure definition for registration with the module system. ///
RawProcedureDefV10 MakeProcedureDef(ITypeRegistrar registrar); - - /// - /// Invokes the procedure with the given arguments and context. - /// - byte[] Invoke(BinaryReader reader, IProcedureContext ctx); } /// diff --git a/crates/bindings-csharp/Runtime/bindings.c b/crates/bindings-csharp/Runtime/bindings.c index de25c99e3e0..d0733aa7ad8 100644 --- a/crates/bindings-csharp/Runtime/bindings.c +++ b/crates/bindings-csharp/Runtime/bindings.c @@ -149,12 +149,12 @@ PREINIT(10, startup) { extern void _start(); _start(); - ffi_class = mono_wasm_assembly_find_class( - mono_wasm_assembly_load("SpacetimeDB.Runtime.dll"), - "SpacetimeDB.Internal", "Module"); - assert(ffi_class && - "FFI export class (SpacetimeDB.Internal.Module) not found"); -} + ffi_class = mono_wasm_assembly_find_class( + mono_wasm_assembly_load("StdbModule.dll"), + "SpacetimeDB", "ModuleRegistration"); + assert(ffi_class && + "FFI export class (SpacetimeDB.ModuleRegistration) not found"); +} #define EXPORT_WITH_MONO_RES(ret, res_code, name, params, args...) \ static MonoMethod* ffi_method_##name; \ From cd4c367eb0768e58c676e02bce6ff83e983abd2d Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Thu, 27 Aug 2026 09:07:23 -0500 Subject: [PATCH 5/6] Fix .NET 8 --- crates/bindings-csharp/Runtime/bindings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bindings-csharp/Runtime/bindings.c b/crates/bindings-csharp/Runtime/bindings.c index d0733aa7ad8..6118ba6be11 100644 --- a/crates/bindings-csharp/Runtime/bindings.c +++ b/crates/bindings-csharp/Runtime/bindings.c @@ -151,9 +151,9 @@ PREINIT(10, startup) { ffi_class = mono_wasm_assembly_find_class( mono_wasm_assembly_load("StdbModule.dll"), - "SpacetimeDB", "ModuleRegistration"); + "", "ModuleRegistration"); assert(ffi_class && - "FFI export class (SpacetimeDB.ModuleRegistration) not found"); + "FFI export class (ModuleRegistration) not found"); } #define EXPORT_WITH_MONO_RES(ret, res_code, name, params, args...) \ From 9101383bc1b10086670861df5372c935ac9308cc Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Thu, 27 Aug 2026 09:14:24 -0500 Subject: [PATCH 6/6] Fix lint --- crates/bindings-csharp/Runtime/Internal/Module.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index 4f72b8be109..177fddd785a 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -386,9 +386,7 @@ public static void RegisterHttpRouter(SpacetimeDB.Router router) { foreach (var route in router.GetRoutes()) { - if ( - !moduleDef.HasHttpHandler(route.HandlerFunction) - ) + if (!moduleDef.HasHttpHandler(route.HandlerFunction)) { throw new ArgumentException( $"HTTP router references unknown handler `{route.HandlerFunction}`", @@ -549,9 +547,12 @@ SpacetimeDB.HttpResponse response ) { var (responseWire, responseBody) = SpacetimeDB.HttpClient.ToWire(response); - responseSink.Write(IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire)); + responseSink.Write( + IStructuralReadWrite.ToBytes(new HttpResponseWire.BSATN(), responseWire) + ); responseBodySink.Write(responseBody); } + public static IReducerContext CreateReducerContext( ulong sender_0, ulong sender_1,