Skip to content

Commit bc0648e

Browse files
committed
Make tests deterministic, csharpier
1 parent 52fbc84 commit bc0648e

File tree

6 files changed

+161
-113
lines changed

6 files changed

+161
-113
lines changed

crates/bindings-csharp/BSATN.Codegen/Type.cs

+110-74
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ namespace SpacetimeDB.Codegen;
88

99
/// <summary>
1010
/// The type of a member of one of the types we are generating code for.
11-
///
11+
///
1212
/// Knows how to serialize and deserialize the member.
13-
///
13+
///
1414
/// Also knows how to compare the member for equality and compute its hash code.
1515
/// We can't just use Equals and GetHashCode for this, because they implement reference
1616
/// equality for arrays and Lists.
17-
///
17+
///
1818
/// (It would be nice to be able to dynamically build EqualityComparers at runtime
19-
/// to do these operations, but this seems to require either (A) reflective calls
19+
/// to do these operations, but this seems to require either (A) reflective calls
2020
/// or (B) instantiating generics at runtime. These are (A) slow and (B) very slow
2121
/// when compiling under IL2CPP. Instead, we just inline the needed loops to compute
2222
/// the relevant values. This is very simple for IL2CPP to optimize.
@@ -25,10 +25,7 @@ namespace SpacetimeDB.Codegen;
2525
/// </summary>
2626
/// <param name="Name">The name of the type</param>
2727
/// <param name="BSATNName">The name of the BSATN struct for the type.</param>
28-
public abstract record TypeUse(
29-
string Name,
30-
string BSATNName
31-
)
28+
public abstract record TypeUse(string Name, string BSATNName)
3229
{
3330
/// <summary>
3431
/// Parse a type use for a member.
@@ -63,13 +60,21 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter
6360
return typeSymbol switch
6461
{
6562
ITypeParameterSymbol => new ReferenceUse(type, typeInfo),
66-
IArrayTypeSymbol { ElementType: var elementType } => new ArrayUse(type, typeInfo, Parse(member, elementType, diag)),
63+
IArrayTypeSymbol { ElementType: var elementType } => new ArrayUse(
64+
type,
65+
typeInfo,
66+
Parse(member, elementType, diag)
67+
),
6768
INamedTypeSymbol named => named.OriginalDefinition.ToString() switch
6869
{
69-
"System.Collections.Generic.List<T>" => new ListUse(type, typeInfo, Parse(member, named.TypeArguments[0], diag)),
70-
_ => named.IsValueType ?
71-
new ValueUse(type, typeInfo) :
72-
new ReferenceUse(type, typeInfo)
70+
"System.Collections.Generic.List<T>" => new ListUse(
71+
type,
72+
typeInfo,
73+
Parse(member, named.TypeArguments[0], diag)
74+
),
75+
_ => named.IsValueType
76+
? new ValueUse(type, typeInfo)
77+
: new ReferenceUse(type, typeInfo),
7378
},
7479
_ => throw new InvalidOperationException($"Unsupported type {type}"),
7580
};
@@ -80,18 +85,24 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter
8085
/// logically-equals is:
8186
/// - the same as to SequenceEquals for arrays and lists
8287
/// - otherwise, just .Equals.
83-
///
88+
///
8489
/// This can't be an expression because some types need to use loops.
8590
/// </summary>
8691
/// <param name="inVar1">A variable of type `Type` that we want to hash.</param>
8792
/// <param name="inVar2">A variable of type `Type` that we want to hash.</param>
8893
/// <param name="outVar">The variable to declare and store the `Equals` bool in.</param>
8994
/// <param name="level">Iteration level counter. You don't need to set this.</param>
9095
/// <returns></returns>
91-
public abstract string EqualsStatement(string inVar1, string inVar2, string outVar, int level = 0);
96+
public abstract string EqualsStatement(
97+
string inVar1,
98+
string inVar2,
99+
string outVar,
100+
int level = 0
101+
);
102+
92103
/// <summary>
93104
/// Get a statement that declares outVar and assigns assigns the hash code of inVar to it.
94-
///
105+
///
95106
/// This can't be an expression because some types need to use loops.
96107
/// </summary>
97108
/// <param name="inVar">A variable of type `Type` that we want to hash.</param>
@@ -101,16 +112,20 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter
101112
public abstract string GetHashCodeStatement(string inVar, string outVar, int level = 0);
102113
}
103114

104-
105115
/// <summary>
106116
/// A use of a value type.
107117
/// </summary>
108118
/// <param name="Type"></param>
109119
/// <param name="TypeInfo"></param>
110120
public record ValueUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)
111121
{
112-
public override string EqualsStatement(string inVar1, string inVar2, string outVar, int level = 0) =>
113-
$"var {outVar} = {inVar1}.Equals({inVar2});";
122+
public override string EqualsStatement(
123+
string inVar1,
124+
string inVar2,
125+
string outVar,
126+
int level = 0
127+
) => $"var {outVar} = {inVar1}.Equals({inVar2});";
128+
114129
public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>
115130
$"var {outVar} = {inVar}.GetHashCode();";
116131
}
@@ -122,8 +137,13 @@ public override string GetHashCodeStatement(string inVar, string outVar, int lev
122137
/// <param name="TypeInfo"></param>
123138
public record ReferenceUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)
124139
{
125-
public override string EqualsStatement(string inVar1, string inVar2, string outVar, int level = 0) =>
126-
$"var {outVar} = {inVar1} == null ? {inVar2} == null : {inVar1}.Equals({inVar2});";
140+
public override string EqualsStatement(
141+
string inVar1,
142+
string inVar2,
143+
string outVar,
144+
int level = 0
145+
) => $"var {outVar} = {inVar1} == null ? {inVar2} == null : {inVar1}.Equals({inVar2});";
146+
127147
public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>
128148
$"var {outVar} = {inVar} == null ? 0 : {inVar}.GetHashCode();";
129149
}
@@ -136,27 +156,37 @@ public override string GetHashCodeStatement(string inVar, string outVar, int lev
136156
/// <param name="ElementType"></param>
137157
public record ArrayUse(string Type, string TypeInfo, TypeUse ElementType) : TypeUse(Type, TypeInfo)
138158
{
139-
public override string EqualsStatement(string inVar1, string inVar2, string outVar, int level = 0)
159+
public override string EqualsStatement(
160+
string inVar1,
161+
string inVar2,
162+
string outVar,
163+
int level = 0
164+
)
140165
{
141166
var iterVar = $"i{level}";
142167
var innerOutVar = $"{outVar}{level + 1}";
143168

144169
return $$"""
145-
var {{outVar}} = true;
146-
if ({{inVar1}} == null || {{inVar2}} == null) {
147-
{{outVar}} = {{inVar1}} == {{inVar2}};
148-
} else if ({{inVar1}}.Length != {{inVar2}}.Length) {
149-
{{outVar}} = false;
150-
} else {
151-
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Length; {{iterVar}}++) {
152-
{{ElementType.EqualsStatement($"{inVar1}[{iterVar}]", $"{inVar2}[{iterVar}]", innerOutVar, level + 1)}}
153-
if (!{{innerOutVar}}) {
154-
{{outVar}} = false;
155-
break;
170+
var {{outVar}} = true;
171+
if ({{inVar1}} == null || {{inVar2}} == null) {
172+
{{outVar}} = {{inVar1}} == {{inVar2}};
173+
} else if ({{inVar1}}.Length != {{inVar2}}.Length) {
174+
{{outVar}} = false;
175+
} else {
176+
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Length; {{iterVar}}++) {
177+
{{ElementType.EqualsStatement(
178+
$"{inVar1}[{iterVar}]",
179+
$"{inVar2}[{iterVar}]",
180+
innerOutVar,
181+
level + 1
182+
)}}
183+
if (!{{innerOutVar}}) {
184+
{{outVar}} = false;
185+
break;
186+
}
156187
}
157188
}
158-
}
159-
""";
189+
""";
160190
}
161191

162192
public override string GetHashCodeStatement(string inVar, string outVar, int level = 0)
@@ -166,16 +196,20 @@ public override string GetHashCodeStatement(string inVar, string outVar, int lev
166196
var innerOutVar = $"{outVar}{level + 1}";
167197

168198
return $$"""
169-
var {{outVar}} = 0;
170-
if ({{inVar}} != null) {
171-
var {{innerHashCode}} = new System.HashCode();
172-
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Length; {{iterVar}}++) {
173-
{{ElementType.GetHashCodeStatement($"{inVar}[{iterVar}]", innerOutVar, level + 1)}}
174-
{{innerHashCode}}.Add({{innerOutVar}});
199+
var {{outVar}} = 0;
200+
if ({{inVar}} != null) {
201+
var {{innerHashCode}} = new System.HashCode();
202+
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Length; {{iterVar}}++) {
203+
{{ElementType.GetHashCodeStatement(
204+
$"{inVar}[{iterVar}]",
205+
innerOutVar,
206+
level + 1
207+
)}}
208+
{{innerHashCode}}.Add({{innerOutVar}});
209+
}
210+
{{outVar}} = {{innerHashCode}}.ToHashCode();
175211
}
176-
{{outVar}} = {{innerHashCode}}.ToHashCode();
177-
}
178-
""";
212+
""";
179213
}
180214
}
181215

@@ -187,7 +221,12 @@ public override string GetHashCodeStatement(string inVar, string outVar, int lev
187221
/// <param name="ElementType"></param>
188222
public record ListUse(string Type, string TypeInfo, TypeUse ElementType) : TypeUse(Type, TypeInfo)
189223
{
190-
public override string EqualsStatement(string inVar1, string inVar2, string outVar, int level = 0)
224+
public override string EqualsStatement(
225+
string inVar1,
226+
string inVar2,
227+
string outVar,
228+
int level = 0
229+
)
191230
{
192231
var iterVar = $"i{level}";
193232
// needed to avoid warnings on list re-reference.
@@ -196,23 +235,23 @@ public override string EqualsStatement(string inVar1, string inVar2, string outV
196235
var innerOutVar = $"{outVar}{level + 1}";
197236

198237
return $$"""
199-
var {{outVar}} = true;
200-
if ({{inVar1}} == null || {{inVar2}} == null) {
201-
{{outVar}} = {{inVar1}} == {{inVar2}};
202-
} else if ({{inVar1}}.Count != {{inVar2}}.Count) {
203-
{{outVar}} = false;
204-
} else {
205-
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Count; {{iterVar}}++) {
206-
var {{innerTmp1}} = {{inVar1}}[{{iterVar}}];
207-
var {{innerTmp2}} = {{inVar2}}[{{iterVar}}];
208-
{{ElementType.EqualsStatement(innerTmp1, innerTmp2, innerOutVar, level + 1)}}
209-
if (!{{innerOutVar}}) {
210-
{{outVar}} = false;
211-
break;
238+
var {{outVar}} = true;
239+
if ({{inVar1}} == null || {{inVar2}} == null) {
240+
{{outVar}} = {{inVar1}} == {{inVar2}};
241+
} else if ({{inVar1}}.Count != {{inVar2}}.Count) {
242+
{{outVar}} = false;
243+
} else {
244+
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Count; {{iterVar}}++) {
245+
var {{innerTmp1}} = {{inVar1}}[{{iterVar}}];
246+
var {{innerTmp2}} = {{inVar2}}[{{iterVar}}];
247+
{{ElementType.EqualsStatement(innerTmp1, innerTmp2, innerOutVar, level + 1)}}
248+
if (!{{innerOutVar}}) {
249+
{{outVar}} = false;
250+
break;
251+
}
212252
}
213253
}
214-
}
215-
""";
254+
""";
216255
}
217256

218257
public override string GetHashCodeStatement(string inVar, string outVar, int level = 0)
@@ -223,17 +262,17 @@ public override string GetHashCodeStatement(string inVar, string outVar, int lev
223262
var innerOutVar = $"{outVar}{level + 1}";
224263

225264
return $$"""
226-
var {{outVar}} = 0;
227-
if ({{inVar}} != null) {
228-
var {{innerHashCode}} = new System.HashCode();
229-
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Count; {{iterVar}}++) {
230-
var {{innerTmp}} = {{inVar}}[{{iterVar}}];
231-
{{ElementType.GetHashCodeStatement(innerTmp, innerOutVar, level + 1)}}
232-
{{innerHashCode}}.Add({{innerOutVar}});
265+
var {{outVar}} = 0;
266+
if ({{inVar}} != null) {
267+
var {{innerHashCode}} = new System.HashCode();
268+
for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Count; {{iterVar}}++) {
269+
var {{innerTmp}} = {{inVar}}[{{iterVar}}];
270+
{{ElementType.GetHashCodeStatement(innerTmp, innerOutVar, level + 1)}}
271+
{{innerHashCode}}.Add({{innerOutVar}});
272+
}
273+
{{outVar}} = {{innerHashCode}}.ToHashCode();
233274
}
234-
{{outVar}} = {{innerHashCode}}.ToHashCode();
235-
}
236-
""";
275+
""";
237276
}
238277
}
239278

@@ -249,10 +288,7 @@ TypeUse Type
249288
)
250289
{
251290
public MemberDeclaration(ISymbol member, ITypeSymbol type, DiagReporter diag)
252-
: this(member.Name, TypeUse.Parse(member, type, diag))
253-
{
254-
255-
}
291+
: this(member.Name, TypeUse.Parse(member, type, diag)) { }
256292

257293
public MemberDeclaration(IFieldSymbol field, DiagReporter diag)
258294
: this(field, field.Type, diag) { }

crates/bindings-csharp/BSATN.Codegen/Utils.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ IncrementalGeneratorInitializationContext context
7373
public static string MakeRwTypeParam(string typeParam) => typeParam + "RW";
7474

7575
public class UnresolvedTypeException(INamedTypeSymbol type)
76-
: InvalidOperationException($"Could not resolve type {type}")
77-
{ }
76+
: InvalidOperationException($"Could not resolve type {type}") { }
7877

7978
/// <summary>
8079
/// Return whether a type is a nullable, non-value type.

0 commit comments

Comments
 (0)