Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
56f81c6
Resolved misc warnings in C# bindings code
rekhoff Sep 29, 2025
9d1f1f0
Corrected formatting errors
rekhoff Sep 29, 2025
8044b75
Add dotnet version for GitHub .Net tests
rekhoff Sep 29, 2025
485de5a
Resolved additional warnings during C# bindings tests
rekhoff Sep 29, 2025
9528c86
Resolved additional warnings during C# bindings tests
rekhoff Sep 29, 2025
64c2c28
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
rekhoff Sep 29, 2025
348aa2a
Resolved additional warnings during C# bindings tests
rekhoff Sep 30, 2025
12a7076
Adds BasicDataRecord to fix CS0246 error
rekhoff Sep 30, 2025
8e9721f
Resolve CS8862 error
rekhoff Sep 30, 2025
185798d
Fixes BSATN.Runtime test error
rekhoff Sep 30, 2025
858c035
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
bfops Sep 30, 2025
b3c7ad5
Updating global.json import method for csharp tests
rekhoff Sep 30, 2025
c2e495e
Removing Android SDK from GitHub runner
rekhoff Sep 30, 2025
2bd5329
Add comment to node cleanup action
rekhoff Sep 30, 2025
420107b
Removes global.json move for csharp-test.yml in favor of #3297
rekhoff Sep 30, 2025
442085a
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
bfops Sep 30, 2025
93dbce0
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
rekhoff Sep 30, 2025
1a19191
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
rekhoff Sep 30, 2025
7341d68
Updated verified files in SDK tests
rekhoff Sep 30, 2025
e7c0e25
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
rekhoff Oct 1, 2025
963fd94
Merge branch 'master' into rekhoff/csharp-bindings-test-error-fixes
rekhoff Oct 1, 2025
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
8 changes: 8 additions & 0 deletions .github/workflows/csharp-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ jobs:
cancel-in-progress: true
timeout-minutes: 30
steps:
- run: df -h
# Free up disk space because otherwise we run out during the test
- name: "node-cleanup"
run: |
sudo rm -rf /usr/local/lib/android
sudo docker image prune --all --force
sudo docker builder prune -a
Copy link
Collaborator

Choose a reason for hiding this comment

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

@bfops Instead of clearing some space here do you think we should just move this job to the custom runner which just has more space available? I know we've been running out of space there as well but I think we can fix that issue separately.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm open to that if it "just works". if we end up having to fight more battles, I'd say keep this as-is and maybe leave a TODO for the future.

Copy link
Collaborator

Choose a reason for hiding this comment

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

trying that in #3318

- run: df -h
- name: Checkout repository
id: checkout-stdb
uses: actions/checkout@v4
Expand Down
22 changes: 11 additions & 11 deletions crates/bindings-csharp/BSATN.Codegen/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -594,16 +594,16 @@ public override string ToString() =>

write = "value.WriteFields(writer);";

var declHashName = (MemberDeclaration decl) => $"___hash{decl.Name}";
string DeclHashName(MemberDeclaration decl) => $"___hash{decl.Name}";

getHashCode = $$"""
{{string.Join("\n", bsatnDecls.Select(decl => decl.Type.GetHashCodeStatement(decl.Name, declHashName(decl))))}}
return {{JoinOrValue(
" ^\n ",
bsatnDecls.Select(declHashName),
"0" // if there are no members, the hash is 0.
)}};
""";
{{string.Join("\n", bsatnDecls.Select(decl => decl.Type.GetHashCodeStatement(decl.Name, DeclHashName(decl))))}}
return {{JoinOrValue(
" ^\n ",
bsatnDecls.Select((Func<MemberDeclaration, string>?)DeclHashName),
"0" // if there are no members, the hash is 0.
)}};
""";
}

extensions.Contents.Append(
Expand Down Expand Up @@ -643,7 +643,7 @@ public override int GetHashCode()
// If we are a reference type, various equality methods need to take nullable references.
// If we are a value type, everything is pleasantly by-value.
var fullNameMaybeRef = $"{FullName}{(Scope.IsStruct ? "" : "?")}";
var declEqualsName = (MemberDeclaration decl) => $"___eq{decl.Name}";
string DeclEqualsName(MemberDeclaration decl) => $"___eq{decl.Name}";

extensions.Contents.Append(
$$"""
Expand All @@ -652,10 +652,10 @@ public override int GetHashCode()
public bool Equals({{fullNameMaybeRef}} that)
{
{{(Scope.IsStruct ? "" : "if (((object?)that) == null) { return false; }\n ")}}
{{string.Join("\n", bsatnDecls.Select(decl => decl.Type.EqualsStatement($"this.{decl.Name}", $"that.{decl.Name}", declEqualsName(decl))))}}
{{string.Join("\n", bsatnDecls.Select(decl => decl.Type.EqualsStatement($"this.{decl.Name}", $"that.{decl.Name}", DeclEqualsName(decl))))}}
return {{JoinOrValue(
" &&\n ",
bsatnDecls.Select(declEqualsName),
bsatnDecls.Select((Func<MemberDeclaration, string>?)DeclEqualsName),
"true" // if there are no elements, the structs are equal :)
)}};
}
Expand Down
31 changes: 11 additions & 20 deletions crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,15 @@ public BasicDataClass((int x, string y, int? z, string? w) data)
}

[Type]
public partial struct BasicDataStruct
public partial struct BasicDataStruct(int x, string y, int? z, string? w)
{
public int X;
public string Y;
public int? Z;
public string? W;
public int X = x;
public string Y = y;
public int? Z = z;
public string? W = w;

public BasicDataStruct((int x, string y, int? z, string? w) data)
{
X = data.x;
Y = data.y;
Z = data.z;
W = data.w;
}
: this(data.x, data.y, data.z, data.w) { }
}

[Type]
Expand Down Expand Up @@ -315,12 +310,12 @@ public void Add(bool collides)
}
}

public double CollisionFraction
public readonly double CollisionFraction
{
get => (double)Collisions / (double)Comparisons;
}

public void AssertCollisionsLessThan(double fraction)
public readonly void AssertCollisionsLessThan(double fraction)
{
Assert.True(
CollisionFraction < fraction,
Expand Down Expand Up @@ -630,14 +625,10 @@ public static void GeneratedNestedListRoundTrip()
static readonly Gen<(ContainsNestedList e1, ContainsNestedList e2)> GenTwoContainsNestedList =
Gen.Select(GenContainsNestedList, GenContainsNestedList, (e1, e2) => (e1, e2));

class EnumerableEqualityComparer<T> : EqualityComparer<IEnumerable<T>>
class EnumerableEqualityComparer<T>(EqualityComparer<T> equalityComparer)
: EqualityComparer<IEnumerable<T>>
{
private readonly EqualityComparer<T> EqualityComparer;

public EnumerableEqualityComparer(EqualityComparer<T> equalityComparer)
{
EqualityComparer = equalityComparer;
}
private readonly EqualityComparer<T> EqualityComparer = equalityComparer;

public override bool Equals(IEnumerable<T>? x, IEnumerable<T>? y) =>
x == null ? y == null : (y == null ? false : x.SequenceEqual(y, EqualityComparer));
Expand Down
13 changes: 3 additions & 10 deletions crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@ public interface ITypeRegistrar
}

[SpacetimeDB.Type]
public partial struct AggregateElement
public partial struct AggregateElement(string? name, AlgebraicType algebraicType)
{
public string? Name;

public AlgebraicType AlgebraicType;

public AggregateElement(string name, AlgebraicType algebraicType)
{
Name = name;
AlgebraicType = algebraicType;
}
public string? Name = name;
public AlgebraicType AlgebraicType = algebraicType;
}

[SpacetimeDB.Type]
Expand Down
47 changes: 39 additions & 8 deletions crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,22 @@ public interface IReadWrite<T>
/// Note: the [Type] macro rejects enums with explicitly set values (see Codegen.Tests),
/// so this array is guaranteed to be continuous and indexed starting from 0.
/// </summary>
private static readonly T[] TagToValue = Enum.GetValues(typeof(T)).Cast<T>().ToArray();
#if NET8_0_OR_GREATER
private static readonly T[] TagToValue = System.Enum.GetValues<T>();
#else
private static readonly T[] TagToValue = CreateTagToValue();

private static T[] CreateTagToValue()
{
var values = System.Enum.GetValues(typeof(T));
var result = new T[values.Length];
for (var i = 0; i < values.Length; i++)
{
result[i] = (T)values.GetValue(i);
}
return result;
}
#endif

public T Read(BinaryReader reader)
{
Expand Down Expand Up @@ -173,15 +188,31 @@ public void Write(BinaryWriter writer, T value)
}
}

public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>
registrar.RegisterType<T>(
public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar)
{
return registrar.RegisterType<T>(
(_) =>
new AlgebraicType.Sum(
Enum.GetNames(typeof(T))
.Select(name => new AggregateElement(name, AlgebraicType.Unit))
.ToArray()
)
{
#if NET8_0_OR_GREATER
return new AlgebraicType.Sum(
[
.. System
.Enum.GetNames<T>()
.Select(name => new AggregateElement(name, AlgebraicType.Unit)),
]
);
#else
var names = System.Enum.GetNames(typeof(T));
var elements = new AggregateElement[names.Length];
for (var i = 0; i < names.Length; i++)
{
elements[i] = new AggregateElement(names[i], AlgebraicType.Unit);
}
return new AlgebraicType.Sum(elements);
#endif
}
);
}
}

public readonly struct RefOption<Inner, InnerRW> : IReadWrite<Inner?>
Expand Down
28 changes: 14 additions & 14 deletions crates/bindings-csharp/BSATN.Runtime/Builtins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ public static implicit operator DateTimeOffset(Timestamp t) =>
DateTimeOffset.UnixEpoch.AddTicks(t.MicrosecondsSinceUnixEpoch * Util.TicksPerMicrosecond);

public static implicit operator Timestamp(DateTimeOffset offset) =>
new Timestamp(offset.Subtract(DateTimeOffset.UnixEpoch).Ticks / Util.TicksPerMicrosecond);
new(offset.Subtract(DateTimeOffset.UnixEpoch).Ticks / Util.TicksPerMicrosecond);

// For backwards-compatibility.
public readonly DateTimeOffset ToStd() => this;
Expand All @@ -347,7 +347,7 @@ public override readonly string ToString()
public static readonly Timestamp UNIX_EPOCH = new(0);

public static Timestamp FromTimeDurationSinceUnixEpoch(TimeDuration timeDuration) =>
new Timestamp(timeDuration.Microseconds);
new(timeDuration.Microseconds);

public readonly TimeDuration ToTimeDurationSinceUnixEpoch() => TimeDurationSince(UNIX_EPOCH);

Expand All @@ -357,15 +357,15 @@ public static Timestamp FromTimeSpanSinceUnixEpoch(TimeSpan timeSpan) =>
public readonly TimeSpan ToTimeSpanSinceUnixEpoch() => (TimeSpan)ToTimeDurationSinceUnixEpoch();

public readonly TimeDuration TimeDurationSince(Timestamp earlier) =>
new TimeDuration(checked(MicrosecondsSinceUnixEpoch - earlier.MicrosecondsSinceUnixEpoch));
new(checked(MicrosecondsSinceUnixEpoch - earlier.MicrosecondsSinceUnixEpoch));

public static Timestamp operator +(Timestamp point, TimeDuration interval) =>
new Timestamp(checked(point.MicrosecondsSinceUnixEpoch + interval.Microseconds));
new(checked(point.MicrosecondsSinceUnixEpoch + interval.Microseconds));

public static Timestamp operator -(Timestamp point, TimeDuration interval) =>
new Timestamp(checked(point.MicrosecondsSinceUnixEpoch - interval.Microseconds));
new(checked(point.MicrosecondsSinceUnixEpoch - interval.Microseconds));

public int CompareTo(Timestamp that)
public readonly int CompareTo(Timestamp that)
{
return this.MicrosecondsSinceUnixEpoch.CompareTo(that.MicrosecondsSinceUnixEpoch);
}
Expand Down Expand Up @@ -466,10 +466,10 @@ public static implicit operator TimeDuration(TimeSpan timeSpan) =>
new(timeSpan.Ticks / Util.TicksPerMicrosecond);

public static TimeDuration operator +(TimeDuration lhs, TimeDuration rhs) =>
new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds));
new(checked(lhs.Microseconds + rhs.Microseconds));

public static TimeDuration operator -(TimeDuration lhs, TimeDuration rhs) =>
new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds));
new(checked(lhs.Microseconds - rhs.Microseconds));

// For backwards-compatibility.
public readonly TimeSpan ToStd() => this;
Expand Down Expand Up @@ -548,7 +548,7 @@ long microsSinceUnixEpoch
// --- auto-generated ---
private ScheduleAt() { }

internal enum @enum : byte
internal enum EnumTag : byte
{
Interval,
Time,
Expand All @@ -560,15 +560,15 @@ public sealed record Time(Timestamp Time_) : ScheduleAt;

public readonly partial struct BSATN : IReadWrite<ScheduleAt>
{
internal static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new();
internal static readonly SpacetimeDB.BSATN.Enum<EnumTag> __enumTag = new();
internal static readonly TimeDuration.BSATN Interval = new();
internal static readonly Timestamp.BSATN Time = new();

public ScheduleAt Read(BinaryReader reader) =>
__enumTag.Read(reader) switch
{
@enum.Interval => new Interval(Interval.Read(reader)),
@enum.Time => new Time(Time.Read(reader)),
EnumTag.Interval => new Interval(Interval.Read(reader)),
EnumTag.Time => new Time(Time.Read(reader)),
_ => throw new InvalidOperationException(
"Invalid tag value, this state should be unreachable."
),
Expand All @@ -579,12 +579,12 @@ public void Write(BinaryWriter writer, ScheduleAt value)
switch (value)
{
case Interval(var inner):
__enumTag.Write(writer, @enum.Interval);
__enumTag.Write(writer, EnumTag.Interval);
Interval.Write(writer, inner);
break;

case Time(var inner):
__enumTag.Write(writer, @enum.Time);
__enumTag.Write(writer, EnumTag.Time);
Time.Write(writer, inner);
break;
}
Expand Down
19 changes: 12 additions & 7 deletions crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,12 @@ public ulong Delete({{argsBounds}}) =>
}
}

public record struct View(string viewName, string tableName, string view, string getter);
public record struct View(
string ViewName,
string TableName,
string ViewCode,
string GetterCode
);

public IEnumerable<View> GenerateViews()
{
Expand Down Expand Up @@ -539,7 +544,7 @@ v.Scheduled is { } scheduled
}
}

public record Constraint(ColumnDeclaration Col, int Pos, ColumnAttrs Attr)
public readonly record struct Constraint(ColumnDeclaration Col, int Pos, ColumnAttrs Attr)
{
public ViewIndex ToIndex() => new(new ColumnRef(Pos, Col.Name));
}
Expand Down Expand Up @@ -853,8 +858,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
tables
.SelectMany((t, ct) => t.GenerateViews())
.WithTrackingName("SpacetimeDB.Table.GenerateViews"),
v => v.viewName,
v => v.tableName
v => v.ViewName,
v => v.TableName
);

var rlsFilters = context
Expand Down Expand Up @@ -919,11 +924,11 @@ internal ReducerContext(Identity identity, ConnectionId? connectionId, Random ra
}

namespace Internal.TableHandles {
{{string.Join("\n", tableViews.Select(v => v.view))}}
{{string.Join("\n", tableViews.Select(v => v.ViewCode))}}
}

public sealed class Local {
{{string.Join("\n", tableViews.Select(v => v.getter))}}
{{string.Join("\n", tableViews.Select(v => v.GetterCode))}}
}
}

Expand All @@ -949,7 +954,7 @@ public static void Main() {
)}}
{{string.Join(
"\n",
tableViews.Select(t => $"SpacetimeDB.Internal.Module.RegisterTable<{t.tableName}, SpacetimeDB.Internal.TableHandles.{t.viewName}>();")
tableViews.Select(t => $"SpacetimeDB.Internal.Module.RegisterTable<{t.TableName}, SpacetimeDB.Internal.TableHandles.{t.ViewName}>();")
)}}
{{string.Join(
"\n",
Expand Down
2 changes: 1 addition & 1 deletion crates/bindings-csharp/Runtime/Internal/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ partial class RawModuleDefV9
// Fix it up to a different mangling scheme if it causes problems.
private static string GetFriendlyName(Type type) =>
type.IsGenericType
? $"{type.Name.Remove(type.Name.IndexOf('`'))}_{string.Join("_", type.GetGenericArguments().Select(GetFriendlyName))}"
? $"{type.Name[..type.Name.IndexOf('`')]}_{string.Join("_", type.GetGenericArguments().Select(GetFriendlyName))}"
: type.Name;

private void RegisterTypeName<T>(AlgebraicType.Ref typeRef)
Expand Down
Loading
Loading