Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ jobs:
./emsdk install 4.0.21
./emsdk activate 4.0.21

# Install WASI SDK for .NET 10 NativeAOT-LLVM compilation.
- name: Install WASI SDK (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.tar.gz
tar -xzf wasi-sdk-29.0-x86_64-linux.tar.gz
sudo mv wasi-sdk-29.0-x86_64-linux /opt/wasi-sdk
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> "$GITHUB_ENV"

- name: Install emscripten (Windows)
if: runner.os == 'Windows'
shell: pwsh
Expand Down
2 changes: 1 addition & 1 deletion crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<PropertyGroup>
<!-- Note: the binary produced by this package is used in Unity too, which is limited to .NET Standard 2.1. -->
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net8.0;net10.0</TargetFrameworks>
<RootNamespace>SpacetimeDB</RootNamespace>
<!-- You can enable this when debugging codegen problems. Outputs in obj/debug/[version]/generated. -->
<!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> -->
Expand Down
33 changes: 33 additions & 0 deletions crates/bindings-csharp/Runtime/Internal/FFI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ namespace SpacetimeDB.Internal;

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
#if EXPERIMENTAL_WASM_AOT && NET10_0_OR_GREATER
using WasmImportLinkageAttribute = System.Runtime.InteropServices.WasmImportLinkageAttribute;
#else
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
file sealed class WasmImportLinkageAttribute : Attribute { }
#endif

// This type is outside of the hidden `FFI` class because for now we need to do some public
// forwarding in the codegen for `__describe_module__` and `__call_reducer__` exports which both
Expand Down Expand Up @@ -190,29 +196,34 @@ public readonly record struct RowIter(uint Handle)
public static readonly RowIter INVALID = new(0);
}

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus table_id_from_name(
[In] byte[] name,
uint name_len,
out TableId out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus index_id_from_name(
[In] byte[] name,
uint name_len,
out IndexId out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_table_row_count(TableId table_id, out ulong out_);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_table_scan_bsatn(
TableId table_id,
out RowIter out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_4)]
public static partial CheckedStatus datastore_index_scan_point_bsatn(
IndexId index_id,
Expand All @@ -221,6 +232,7 @@ public static partial CheckedStatus datastore_index_scan_point_bsatn(
out RowIter out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_4)]
public static partial CheckedStatus datastore_delete_by_index_scan_point_bsatn(
IndexId index_id,
Expand All @@ -229,6 +241,7 @@ public static partial CheckedStatus datastore_delete_by_index_scan_point_bsatn(
out uint out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_index_scan_range_bsatn(
IndexId index_id,
Expand All @@ -242,23 +255,27 @@ public static partial CheckedStatus datastore_index_scan_range_bsatn(
out RowIter out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial Errno row_iter_bsatn_advance(
RowIter iter_handle,
[MarshalUsing(CountElementName = nameof(buffer_len))] [Out] byte[] buffer,
ref uint buffer_len
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus row_iter_bsatn_close(RowIter iter_handle);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_insert_bsatn(
TableId table_id,
Span<byte> row,
ref uint row_len
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_update_bsatn(
TableId table_id,
Expand All @@ -267,6 +284,7 @@ public static partial CheckedStatus datastore_update_bsatn(
ref uint row_len
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_delete_by_index_scan_range_bsatn(
IndexId index_id,
Expand All @@ -280,6 +298,7 @@ public static partial CheckedStatus datastore_delete_by_index_scan_range_bsatn(
out uint out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus datastore_delete_all_by_eq_bsatn(
TableId table_id,
Expand All @@ -288,16 +307,19 @@ public static partial CheckedStatus datastore_delete_all_by_eq_bsatn(
out uint out_
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_5)]
public static partial CheckedStatus datastore_clear(TableId table_id, out ulong out_);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial Errno bytes_source_read(
BytesSource source,
Span<byte> buffer,
ref uint buffer_len
);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus bytes_sink_write(
BytesSink sink,
Expand All @@ -315,6 +337,7 @@ public enum LogLevel : byte
Panic = 5,
}

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial void console_log(
LogLevel level,
Expand Down Expand Up @@ -352,12 +375,15 @@ internal static class ConsoleTimerIdMarshaller
}
}

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial ConsoleTimerId console_timer_start([In] byte[] name, uint name_len);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial CheckedStatus console_timer_end(ConsoleTimerId stopwatch_id);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_0)]
public static partial void volatile_nonatomic_schedule_immediate(
[In] byte[] name,
Expand All @@ -374,22 +400,28 @@ uint args_len
// which prevents source-generated PInvokes from working with types from other assemblies, and
// `Identity` lives in another assembly (`BSATN.Runtime`). Luckily, `DllImport` is enough here.
#pragma warning disable SYSLIB1054 // Suppress "Use 'LibraryImportAttribute' instead of 'DllImportAttribute'" warning.
[WasmImportLinkage]
[DllImport(StdbNamespace10_0)]
public static extern void identity(out Identity dest);
#pragma warning restore SYSLIB1054

[WasmImportLinkage]
[DllImport(StdbNamespace10_1)]
public static extern Errno bytes_source_remaining_length(BytesSource source, ref uint len);

[WasmImportLinkage]
[DllImport(StdbNamespace10_2)]
public static extern Errno get_jwt(ref ConnectionId connectionId, out BytesSource source);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_3, EntryPoint = "procedure_start_mut_tx")]
public static partial Errno procedure_start_mut_tx(out long micros);

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_3, EntryPoint = "procedure_commit_mut_tx")]
public static partial Errno procedure_commit_mut_tx();

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_3, EntryPoint = "procedure_abort_mut_tx")]
public static partial Errno procedure_abort_mut_tx();

Expand All @@ -400,6 +432,7 @@ public readonly struct BytesSourcePair
public readonly BytesSource B;
}

[WasmImportLinkage]
[LibraryImport(StdbNamespace10_3, EntryPoint = "procedure_http_request")]
public static partial Errno procedure_http_request(
ReadOnlySpan<byte> request,
Expand Down
15 changes: 9 additions & 6 deletions crates/bindings-csharp/Runtime/Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
</PropertyGroup>

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RootNamespace>SpacetimeDB</RootNamespace>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<RestoreAdditionalProjectSources Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'net10.0'">
<DefineConstants>$(DefineConstants);EXPERIMENTAL_WASM_AOT</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand All @@ -27,10 +31,9 @@
</ItemGroup>

<!-- These must be explicit package dependencies so NuGet consumers can resolve the LLVM toolchain. -->
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" IncludeAssets="All" />
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" IncludeAssets="All" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" IncludeAssets="All" />
</ItemGroup>

<ItemGroup>
Expand Down
19 changes: 17 additions & 2 deletions crates/bindings-csharp/Runtime/bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,24 @@ WASI_SHIM(path_remove_directory, (int32_t, int32_t, int32_t));
WASI_SHIM(path_rename, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));
WASI_SHIM(path_symlink, (int32_t, int32_t, int32_t, int32_t, int32_t));
WASI_SHIM(path_unlink_file, (int32_t, int32_t, int32_t));
WASI_SHIM(poll_oneoff, (int32_t, int32_t, int32_t, int32_t));
int32_t WASI_NAME(poll_oneoff)(int32_t, int32_t, int32_t, int32_t nevents_ptr) {
if (nevents_ptr) {
*(__wasi_size_t*)(uintptr_t)nevents_ptr = 0;
}
// Returning success with uninitialized events can wedge the runtime.
// Fail explicitly so the caller surfaces the missing capability instead.
return __WASI_ERRNO_NOSYS;
}
WASI_SHIM(sched_yield, ());
WASI_SHIM(random_get, (int32_t, int32_t));
int32_t WASI_NAME(random_get)(int32_t buf, int32_t len) {
static uint32_t state = 0x13579BDFu;
uint8_t* out = (uint8_t*)(uintptr_t)buf;
for (int32_t i = 0; i < len; i++) {
state = state * 1664525u + 1013904223u;
out[i] = (uint8_t)(state >> 24);
}
return 0;
}
WASI_SHIM(sock_accept, (int32_t, int32_t, int32_t));
WASI_SHIM(sock_recv, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));
WASI_SHIM(sock_send, (int32_t, int32_t, int32_t, int32_t, int32_t));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
<DefineConstants>$(DefineConstants);EXPERIMENTAL_WASM_AOT</DefineConstants>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<UseAppHost>false</UseAppHost>
<PublishTrimmed>true</PublishTrimmed>
<SelfContained>true</SelfContained>
<WasmEnableThreads>false</WasmEnableThreads>
<SpacetimeNamespace>spacetime_10.0</SpacetimeNamespace>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
Expand Down
40 changes: 38 additions & 2 deletions crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
Project="$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets"
Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1' and '$(ILCompilerTargetsPath)' == '' and '$(PkgMicrosoft_DotNet_ILCompiler_LLVM)' != '' and Exists('$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets')" />

<!-- Override IlcLlvmTarget AFTER the ILCompiler.LLVM.targets import (which defaults to wasip2).
SpacetimeDB's host only supports WASI Preview 1 (wasip1). -->
<PropertyGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<IlcLlvmTarget>wasm32-unknown-wasip1</IlcLlvmTarget>
</PropertyGroup>

<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<NativeLibrary Include="$(MSBuildThisFileDirectory)..\bindings.c" />
<UnmanagedEntryPointsAssembly Include="SpacetimeDB.Runtime" />
Expand Down Expand Up @@ -46,6 +52,9 @@
<WasmImport Include="spacetime_10.4!datastore_index_scan_point_bsatn" />
<WasmImport Include="spacetime_10.4!datastore_delete_by_index_scan_point_bsatn" />

<!-- spacetime_10.5 imports -->
<WasmImport Include="spacetime_10.5!datastore_clear" />

<CustomLinkerArg Include="-DEXPERIMENTAL_WASM_AOT" />
</ItemGroup>

Expand All @@ -54,13 +63,40 @@
<_WasmNativeFileForLinking Include="@(NativeFileReference)" />
</ItemGroup>

<!-- Strip .wit files from the WASI runtime overlay to prevent NativeAOT-LLVM from
generating WebAssembly Component Model exports (wasip2). SpacetimeDB's host only
supports WASI Preview 1 (wasip1), so component-model exports cause failures. -->
<Target Name="UseWasiRuntimeOverlayWithoutComponentWit"
Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'"
AfterTargets="SetupProperties"
BeforeTargets="LinkNativeLlvm">
<PropertyGroup>
<_OriginalIlcSdkPath>$(IlcSdkPath)</_OriginalIlcSdkPath>
<_WasiRuntimeOverlayDir>$(IntermediateOutputPath)native-hidden-no-wit\</_WasiRuntimeOverlayDir>
</PropertyGroup>

<ItemGroup>
<_WasiRuntimeOverlaySource Include="$(IlcFrameworkNativePath)**\*" Exclude="$(IlcFrameworkNativePath)**\*.wit" />
</ItemGroup>

<RemoveDir Directories="$(_WasiRuntimeOverlayDir)" />
<MakeDir Directories="$(_WasiRuntimeOverlayDir)" />
<Copy SourceFiles="@(_WasiRuntimeOverlaySource)"
DestinationFiles="@(_WasiRuntimeOverlaySource->'$(_WasiRuntimeOverlayDir)%(RecursiveDir)%(Filename)%(Extension)')" />

<PropertyGroup>
<IlcFrameworkNativePath>$(_WasiRuntimeOverlayDir)</IlcFrameworkNativePath>
<IlcSdkPath>$(_OriginalIlcSdkPath)</IlcSdkPath>
</PropertyGroup>
</Target>

<!-- Auto-download WASI SDK on the first run. -->
<!-- Adapted from https://github.com/dotnet/dotnet-wasi-sdk/blob/2dbb00c779180873d3ed985e59e431f56404d8da/src/Wasi.Sdk/build/Wasi.Sdk.targets#L245-L262 -->
<!-- Executes before the errors in https://github.com/dotnet/runtime/blob/57fd56a99d4c97ac2f95fe84640f2a3f653f4dd7/src/mono/wasi/build/WasiApp.Native.targets#L41-L50. -->
<!-- TODO: remove when https://github.com/dotnet/runtime/issues/82788 is resolved. -->
<Target Name="ObtainWasiSdk" BeforeTargets="_SetupWasiSdk;CheckWasmSdks">
<Target Name="ObtainWasiSdk" BeforeTargets="_SetupWasiSdk;CheckWasmSdks;_InitializeWasiSdk">
<PropertyGroup>
<WasiSdkVersion>24</WasiSdkVersion>
<WasiSdkVersion>29</WasiSdkVersion>

<WasiSdkDownloadTempFile>$([System.IO.Path]::Combine($(IntermediateOutputPath), "wasi-sdk.$(WasiSdkVersion).tar.gz"))</WasiSdkDownloadTempFile>

Expand Down
Loading
Loading