1
1
namespace SpacetimeDB . Codegen ;
2
2
3
+ // Generate code to implement serialization to the BSATN format (https://spacetimedb.com/docs/bsatn).
4
+ // C# doesn't support static methods in interfaces, so instead we declare a zero-sized `struct` type that implements
5
+ // the serialization interface (IReadWrite) for us.
6
+ //
7
+ // See BSATN.Runtime for the support code referenced by code generation,
8
+ // and see Codegen.Tests/fixtures/*/snapshots for examples of generated code.
9
+ // Also, if you set <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> in a csproj,
10
+ // you can find the generated code in obj/Debug/*/generated/SpacetimeDB.BSATN.Codegen.
11
+
3
12
using System . Collections . Immutable ;
4
13
using Microsoft . CodeAnalysis ;
5
14
using Microsoft . CodeAnalysis . CSharp ;
@@ -27,6 +36,14 @@ namespace SpacetimeDB.Codegen;
27
36
/// <param name="BSATNName">The name of the BSATN struct for the type.</param>
28
37
public abstract record TypeUse ( string Name , string BSATNName )
29
38
{
39
+ internal static string BSATN_FIELD_SUFFIX = "RW" ;
40
+
41
+ /// <summary>
42
+ /// The name of the static field containing an IReadWrite in the IReadWrite struct associated with this type.
43
+ /// We make sure this is different from the field name so that collisions cannot occur.
44
+ /// </summary>
45
+ public static string BsatnFieldSuffix => $ "{ BSATN_FIELD_SUFFIX } ";
46
+
30
47
/// <summary>
31
48
/// Parse a type use for a member.
32
49
/// </summary>
@@ -330,14 +347,20 @@ IEnumerable<MemberDeclaration> members
330
347
var visStr = SyntaxFacts . GetText ( visibility ) ;
331
348
return string . Join (
332
349
"\n " ,
333
- members . Select ( m => $ "{ visStr } static readonly { m . Type . BSATNName } { m . Name } = new();")
350
+ members . Select ( m =>
351
+ $ "{ visStr } static readonly { m . Type . BSATNName } { m . Name } { TypeUse . BsatnFieldSuffix } = new();"
352
+ )
334
353
) ;
335
354
}
336
355
337
356
public static string GenerateDefs ( IEnumerable < MemberDeclaration > members ) =>
338
357
string . Join (
339
358
",\n " ,
340
- members . Select ( m => $ "new(nameof({ m . Name } ), { m . Name } .GetAlgebraicType(registrar))")
359
+ // we can't use nameof(m.Type.BsatnFieldName) because the bsatn field name differs from the logical name
360
+ // assigned in the type.
361
+ members . Select ( m =>
362
+ $ "new(\" { m . Name } \" , { m . Name } { TypeUse . BsatnFieldSuffix } .GetAlgebraicType(registrar))"
363
+ )
341
364
) ;
342
365
}
343
366
@@ -450,8 +473,6 @@ public Scope.Extensions ToExtensions()
450
473
var extensions = new Scope . Extensions ( Scope , FullName ) ;
451
474
452
475
var bsatnDecls = Members . Cast < MemberDeclaration > ( ) ;
453
- var fieldNames = bsatnDecls . Select ( m => m . Name ) ;
454
- var fieldNamesAndIds = fieldNames . Select ( ( name , i ) => ( name , i ) ) ;
455
476
456
477
extensions . BaseTypes . Add ( $ "System.IEquatable<{ ShortName } >") ;
457
478
@@ -480,8 +501,8 @@ public override string ToString() =>
480
501
return reader.ReadByte() switch {
481
502
{{ string . Join (
482
503
"\n " ,
483
- fieldNames . Select ( ( name , i ) =>
484
- $ "{ i } => new { name } ({ name } .Read(reader)),"
504
+ bsatnDecls . Select ( ( m , i ) =>
505
+ $ "{ i } => new { m . Name } ({ m . Name } { TypeUse . BsatnFieldSuffix } .Read(reader)),"
485
506
)
486
507
) }}
487
508
_ => throw new System.InvalidOperationException("Invalid tag value, this state should be unreachable.")
@@ -492,12 +513,12 @@ public override string ToString() =>
492
513
switch (value) {
493
514
{{ string . Join (
494
515
"\n " ,
495
- fieldNames . Select ( ( name , i ) => $ """
496
- case { name } (var inner):
497
- writer.Write((byte){ i } );
498
- { name } .Write(writer, inner);
499
- break;
500
- """ ) ) }}
516
+ bsatnDecls . Select ( ( m , i ) => $ """
517
+ case { m . Name } (var inner):
518
+ writer.Write((byte){ i } );
519
+ { m . Name } { TypeUse . BsatnFieldSuffix } .Write(writer, inner);
520
+ break;
521
+ """ ) ) }}
501
522
}
502
523
""" ;
503
524
@@ -530,14 +551,14 @@ public override string ToString() =>
530
551
public void ReadFields(System.IO.BinaryReader reader) {
531
552
{{ string . Join (
532
553
"\n " ,
533
- fieldNames . Select ( name => $ " { name } = BSATN.{ name } .Read(reader);")
554
+ bsatnDecls . Select ( m => $ " { m . Name } = BSATN.{ m . Name } { TypeUse . BsatnFieldSuffix } .Read(reader);")
534
555
) }}
535
556
}
536
557
537
558
public void WriteFields(System.IO.BinaryWriter writer) {
538
559
{{ string . Join (
539
560
"\n " ,
540
- fieldNames . Select ( name => $ " BSATN.{ name } . Write(writer, { name } );")
561
+ bsatnDecls . Select ( m => $ " BSATN.{ m . Name } { TypeUse . BsatnFieldSuffix } . Write(writer, { m . Name } );")
541
562
) }}
542
563
}
543
564
@@ -557,7 +578,7 @@ object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer() {
557
578
public override string ToString() =>
558
579
$"{{ ShortName }} {{ start }} {{ string . Join (
559
580
", " ,
560
- fieldNames . Select ( name => $$ """ {{ name }} = {SpacetimeDB.BSATN.StringUtil.GenericToString({{ name }} )}""" )
581
+ bsatnDecls . Select ( m => $$ """ {{ m . Name }} = {SpacetimeDB.BSATN.StringUtil.GenericToString({{ m . Name }} )}""" )
561
582
) }} {{ end }} ";
562
583
"""
563
584
) ;
0 commit comments