Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: add experimental NativeAOT-LLVM support #713

Merged
merged 3 commits into from Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
246 changes: 128 additions & 118 deletions crates/bindings-csharp/Codegen/Module.cs
Expand Up @@ -26,89 +26,86 @@ public class Module : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var tables = context
.SyntaxProvider
.ForAttributeWithMetadataName(
fullyQualifiedMetadataName: "SpacetimeDB.TableAttribute",
predicate: (node, ct) => true, // already covered by attribute restrictions
transform: (context, ct) =>
{
var table = (TypeDeclarationSyntax)context.TargetNode;
var tables = context.SyntaxProvider.ForAttributeWithMetadataName(
fullyQualifiedMetadataName: "SpacetimeDB.TableAttribute",
predicate: (node, ct) => true, // already covered by attribute restrictions
transform: (context, ct) =>
{
var table = (TypeDeclarationSyntax)context.TargetNode;

var resolvedTable =
(ITypeSymbol?)context.SemanticModel.GetDeclaredSymbol(table)
?? throw new System.Exception("Could not resolve table");
var resolvedTable =
(ITypeSymbol?)context.SemanticModel.GetDeclaredSymbol(table)
?? throw new System.Exception("Could not resolve table");

var fields = resolvedTable
.GetMembers()
.OfType<IFieldSymbol>()
.Where(f => !f.IsStatic)
.Select(f =>
{
var indexKind = f.GetAttributes()
.Where(a =>
a.AttributeClass?.ToDisplayString() == "SpacetimeDB.ColumnAttribute"
)
.Select(a => (ColumnAttrs)a.ConstructorArguments[0].Value!)
.SingleOrDefault();

var fields = resolvedTable
.GetMembers()
.OfType<IFieldSymbol>()
.Where(f => !f.IsStatic)
.Select(f =>
if (indexKind.HasFlag(ColumnAttrs.AutoInc))
{
var indexKind = f.GetAttributes()
.Where(
a =>
a.AttributeClass?.ToDisplayString()
== "SpacetimeDB.ColumnAttribute"
)
.Select(a => (ColumnAttrs)a.ConstructorArguments[0].Value!)
.SingleOrDefault();

if (indexKind.HasFlag(ColumnAttrs.AutoInc))
var isValidForAutoInc = f.Type.SpecialType switch
{
var isValidForAutoInc = f.Type.SpecialType switch
{
SpecialType.System_Byte
or SpecialType.System_SByte
or SpecialType.System_Int16
or SpecialType.System_UInt16
or SpecialType.System_Int32
or SpecialType.System_UInt32
or SpecialType.System_Int64
or SpecialType.System_UInt64
=> true,
SpecialType.None
=> f.Type.ToString() switch
{
"System.Int128" or "System.UInt128" => true,
_ => false
},
_ => false
};

if (!isValidForAutoInc)
{
throw new System.Exception(
$"Type {f.Type} is not valid for AutoInc or Identity as it's not an integer."
);
}
SpecialType.System_Byte
or SpecialType.System_SByte
or SpecialType.System_Int16
or SpecialType.System_UInt16
or SpecialType.System_Int32
or SpecialType.System_UInt32
or SpecialType.System_Int64
or SpecialType.System_UInt64
=> true,
SpecialType.None
=> f.Type.ToString() switch
{
"System.Int128" or "System.UInt128" => true,
_ => false
},
_ => false
};

if (!isValidForAutoInc)
{
throw new System.Exception(
$"Type {f.Type} is not valid for AutoInc or Identity as it's not an integer."
);
}
}

return (
Name: f.Name,
Type: SymbolToName(f.Type),
TypeInfo: GetTypeInfo(f.Type),
IndexKind: indexKind
);
})
.ToArray();
return (
Name: f.Name,
Type: SymbolToName(f.Type),
TypeInfo: GetTypeInfo(f.Type),
IndexKind: indexKind
);
})
.ToArray();

return new
{
Scope = new Scope(table),
Name = table.Identifier.Text,
FullName = SymbolToName(context.SemanticModel.GetDeclaredSymbol(table)!),
Fields = fields,
};
}
);
return new
{
Scope = new Scope(table),
Name = table.Identifier.Text,
FullName = SymbolToName(context.SemanticModel.GetDeclaredSymbol(table)!),
Fields = fields,
};
}
);

tables
.Select(
(t, ct) =>
{
var autoIncFields = t.Fields
.Where(f => f.IndexKind.HasFlag(ColumnAttrs.AutoInc))
var autoIncFields = t.Fields.Where(f =>
f.IndexKind.HasFlag(ColumnAttrs.AutoInc)
)
.Select(f => f.Name);

var extensions =
Expand Down Expand Up @@ -197,50 +194,44 @@ or SpecialType.System_UInt64

var tableNames = tables.Select((t, ct) => t.FullName).Collect();

var reducers = context
.SyntaxProvider
.ForAttributeWithMetadataName(
fullyQualifiedMetadataName: "SpacetimeDB.ReducerAttribute",
predicate: (node, ct) => true, // already covered by attribute restrictions
transform: (context, ct) =>
{
var method = (IMethodSymbol)
context.SemanticModel.GetDeclaredSymbol(context.TargetNode)!;
var reducers = context.SyntaxProvider.ForAttributeWithMetadataName(
fullyQualifiedMetadataName: "SpacetimeDB.ReducerAttribute",
predicate: (node, ct) => true, // already covered by attribute restrictions
transform: (context, ct) =>
{
var method = (IMethodSymbol)
context.SemanticModel.GetDeclaredSymbol(context.TargetNode)!;

if (!method.ReturnsVoid)
{
throw new System.Exception($"Reducer {method} must return void");
}
if (!method.ReturnsVoid)
{
throw new System.Exception($"Reducer {method} must return void");
}

var exportName = (string?)
context
.Attributes
.SingleOrDefault()
?.ConstructorArguments
.SingleOrDefault()
.Value;
var exportName = (string?)
context
.Attributes.SingleOrDefault()
?.ConstructorArguments
.SingleOrDefault()
.Value;

return new
{
Name = method.Name,
ExportName = exportName ?? method.Name,
FullName = SymbolToName(method),
Args = method
.Parameters
.Select(
p =>
(
p.Name,
p.Type,
IsDbEvent: p.Type.ToString()
== "SpacetimeDB.Runtime.DbEventArgs"
)
return new
{
Name = method.Name,
ExportName = exportName ?? method.Name,
FullName = SymbolToName(method),
Args = method
.Parameters.Select(p =>
(
p.Name,
p.Type,
IsDbEvent: p.Type.ToString() == "SpacetimeDB.Runtime.DbEventArgs"
)
.ToArray(),
Scope = new Scope((TypeDeclarationSyntax)context.TargetNode.Parent!)
};
}
);
)
.ToArray(),
Scope = new Scope((TypeDeclarationSyntax)context.TargetNode.Parent!)
};
}
);

var addReducers = reducers
.Select(
Expand Down Expand Up @@ -286,26 +277,45 @@ class {r.Name}: IReducer {{
// <auto-generated />
#nullable enable

using static SpacetimeDB.RawBindings;
using SpacetimeDB.Module;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SpacetimeDB.Runtime;
using System.Diagnostics.CodeAnalysis;

using Buffer = SpacetimeDB.RawBindings.Buffer;

static class ModuleRegistration {{
{string.Join("\n", addReducers.Select(r => r.Class))}

#pragma warning disable CA2255
// [ModuleInitializer] - doesn't work because assemblies are loaded lazily;
// might make use of it later down the line, but for now assume there is only one
// module so we can use `Main` instead.

#if EXPERIMENTAL_WASM_AOT
// In AOT mode we're building a library.
// Main method won't be called automatically, so we need to export it as a preinit function.
[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.All, typeof(SpacetimeDB.Module.FFI))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(FFI))]
#endif
public static void Main() {{
{string.Join("\n", addReducers.Select(r => $"FFI.RegisterReducer(new {r.Name}());"))}
{string.Join("\n", tableNames.Select(t => $"FFI.RegisterTable({t}.MakeTableDesc());"))}
}}
#pragma warning restore CA2255

// Exports only work from the main assembly, so we need to generate forwarding methods.
#if EXPERIMENTAL_WASM_AOT
[UnmanagedCallersOnly(EntryPoint = ""__describe_module__"")]
public static Buffer __describe_module__() => FFI.__describe_module__();

[UnmanagedCallersOnly(EntryPoint = ""__call_reducer__"")]
public static Buffer __call_reducer__(
uint id,
Buffer caller_identity,
Buffer caller_address,
ulong timestamp,
Buffer args
) => FFI.__call_reducer__(id, caller_identity, caller_address, timestamp, args);
#endif
}}
"
);
Expand Down
15 changes: 5 additions & 10 deletions crates/bindings-csharp/Runtime/Module.cs
Expand Up @@ -127,14 +127,11 @@ public TableDef(string tableName, ColumnDefWithAttrs[] columns)
// Important: the position must be stored here, before filtering.
.Select((col, pos) => (col, pos))
.Where(pair => pair.col.Attrs != ColumnAttrs.UnSet)
.Select(
pair =>
new ConstraintDef(
$"ct_{tableName}_{pair.col.ColumnDef.ColName}_{pair.col.Attrs}",
pair.col.Attrs,
new[] { (uint)pair.pos }
)
)
.Select(pair => new ConstraintDef(
$"ct_{tableName}_{pair.col.ColumnDef.ColName}_{pair.col.Attrs}",
pair.col.Attrs,
new[] { (uint)pair.pos }
))
.ToArray();
TableType = "user";
TableAccess = tableName.StartsWith('_') ? "private" : "public";
Expand Down Expand Up @@ -276,7 +273,6 @@ public static void RegisterReducer(IReducer reducer)
bool anonymous = false
) => module.SetTypeRef<T>(typeRef, type, anonymous);

// [UnmanagedCallersOnly(EntryPoint = "__describe_module__")]
public static RawBindings.Buffer __describe_module__()
{
// replace `module` with a temporary internal module that will register ModuleDef, AlgebraicType and other internal types
Expand All @@ -300,7 +296,6 @@ public static RawBindings.Buffer __describe_module__()
}
}

// [UnmanagedCallersOnly(EntryPoint = "__call_reducer__")]
public static RawBindings.Buffer __call_reducer__(
uint id,
RawBindings.Buffer caller_identity,
Expand Down
8 changes: 7 additions & 1 deletion crates/bindings-csharp/Runtime/RawBindings.cs
Expand Up @@ -12,7 +12,13 @@ public static partial class RawBindings
// For now this must match the name of the `.c` file (`bindings.c`).
// In the future C# will allow to specify Wasm import namespace in
// `LibraryImport` directly.
const string StdbNamespace = "bindings";
const string StdbNamespace =
#if EXPERIMENTAL_WASM_AOT
"spacetime_7.0"
RReverser marked this conversation as resolved.
Show resolved Hide resolved
#else
"bindings"
#endif
;

// This custom marshaller takes care of checking the status code
// returned from the host and throwing an exception if it's not 0.
Expand Down