using System; using System.Collections.Generic; using System.Linq; using CapnpC.CSharp.Generator.Model; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; namespace CapnpC.CSharp.Generator.CodeGen { class DomainClassSnippetGen { readonly GenNames _names; public DomainClassSnippetGen(GenNames names) { _names = names; } MemberDeclarationSyntax MakeUnionField(Field field) { var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRefAndValue); switch (field.Type.Tag) { case TypeTag.Void: return null; default: return PropertyDeclaration(type, _names.GetCodeIdentifier(field).Identifier) .AddModifiers(Public).AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithExpressionBody( ArrowExpressionClause( ConditionalExpression( BinaryExpression( SyntaxKind.EqualsExpression, _names.UnionDiscriminatorField.IdentifierName, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName)), CastExpression(type, _names.UnionContentField.IdentifierName), LiteralExpression( SyntaxKind.NullLiteralExpression)))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)), AccessorDeclaration( SyntaxKind.SetAccessorDeclaration) .WithBody( Block( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.UnionDiscriminatorField.IdentifierName, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName))), ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.UnionContentField.IdentifierName, IdentifierName("value")))))); } } MemberDeclarationSyntax MakeStructField(Field field) { if (field.Type.Tag == TypeTag.Void) { return null; } var prop = PropertyDeclaration( _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef), _names.GetCodeIdentifier(field).Identifier) .AddModifiers(Public).AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); if (field.DefaultValueIsExplicit && field.Type.IsValueType) { prop = prop.WithInitializer( EqualsValueClause(MakeDefaultValue(field))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); } return prop; } MemberDeclarationSyntax MakeUnionDiscriminatorField() { return FieldDeclaration( VariableDeclaration(_names.UnionDiscriminatorEnum.IdentifierName) .AddVariables( VariableDeclarator(_names.UnionDiscriminatorField.Identifier) .WithInitializer( EqualsValueClause( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.UnionDiscriminatorUndefined.IdentifierName))))) .AddModifiers(Private); } MemberDeclarationSyntax MakeUnionContentField() { return FieldDeclaration( VariableDeclaration(_names.Type(Nullability.NullableRef)) .WithVariables( SingletonSeparatedList( VariableDeclarator(_names.UnionContentField.Identifier)))) .AddModifiers(Private); } IEnumerable MakeInitializerAssignments(Value structValue, TypeDefinition scope) { foreach (var fieldValue in structValue.Fields) { var valueExpr = MakeValue(fieldValue.Item2, scope); if (valueExpr == null) continue; yield return AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.GetCodeIdentifier(fieldValue.Item1).IdentifierName, valueExpr); } } ExpressionSyntax MakeValue(Value value, TypeDefinition scope) { switch (value.Type.Tag) { case TypeTag.AnyEnum: return LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal((ushort)value.ScalarValue)); case TypeTag.Bool: if ((bool)value.ScalarValue) return LiteralExpression(SyntaxKind.TrueLiteralExpression); else return LiteralExpression(SyntaxKind.FalseLiteralExpression); case TypeTag.Data: return ArrayCreationExpression(ArrayType( PredefinedType(Token(SyntaxKind.ByteKeyword))) .WithRankSpecifiers( SingletonList( ArrayRankSpecifier( SingletonSeparatedList( OmittedArraySizeExpression()))))) .WithInitializer( InitializerExpression( SyntaxKind.ArrayInitializerExpression) .AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray())); case TypeTag.Enum: return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, _names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant, Nullability.NonNullable), IdentifierName(value.GetEnumerant().Literal)); case TypeTag.F32: switch ((float)value.ScalarValue) { case float.NaN: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("float"), IdentifierName(nameof(float.NaN))); case float.NegativeInfinity: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("float"), IdentifierName(nameof(float.NegativeInfinity))); case float.PositiveInfinity: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("float"), IdentifierName(nameof(float.PositiveInfinity))); default: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((float)value.ScalarValue)); } case TypeTag.F64: switch ((double)value.ScalarValue) { case double.NaN: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("double"), IdentifierName(nameof(double.NaN))); case double.NegativeInfinity: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("double"), IdentifierName(nameof(double.NegativeInfinity))); case double.PositiveInfinity: return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("double"), IdentifierName(nameof(double.PositiveInfinity))); default: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((double)value.ScalarValue)); } case TypeTag.S8: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((sbyte)value.ScalarValue)); case TypeTag.S16: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((short)value.ScalarValue)); case TypeTag.S32: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((int)value.ScalarValue)); case TypeTag.S64: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((long)value.ScalarValue)); case TypeTag.U8: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((byte)value.ScalarValue)); case TypeTag.U16: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((ushort)value.ScalarValue)); case TypeTag.U32: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((uint)value.ScalarValue)); case TypeTag.U64: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((ulong)value.ScalarValue)); case TypeTag.Text: value.Decode(); return value.ScalarValue == null ? LiteralExpression(SyntaxKind.NullLiteralExpression) : LiteralExpression(SyntaxKind.StringLiteralExpression, Literal((string)value.ScalarValue)); case TypeTag.Group: case TypeTag.Struct: value.Decode(); return ObjectCreationExpression( _names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass, Nullability.NonNullable)) .WithArgumentList(ArgumentList()) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression) .AddExpressions(MakeInitializerAssignments(value, scope).ToArray())); case TypeTag.ListPointer: // TBD return LiteralExpression(SyntaxKind.NullLiteralExpression); case TypeTag.List when value.Type.ElementType.Tag == TypeTag.Void: value.Decode(); return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((int)value.VoidListCount)); case TypeTag.List: value.Decode(); return ArrayCreationExpression(ArrayType( _names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass, Nullability.NullableRef)) .WithRankSpecifiers( SingletonList( ArrayRankSpecifier( SingletonSeparatedList( OmittedArraySizeExpression()))))) .WithInitializer( InitializerExpression( SyntaxKind.ArrayInitializerExpression) .AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray())); case TypeTag.AnyPointer: case TypeTag.CapabilityPointer: // TBD return null; case TypeTag.Interface: return null; default: throw new NotImplementedException(); } } ExpressionSyntax MakeDefaultValue(Field field) { if (field.DefaultValueIsExplicit) { return MakeValue(field.DefaultValue, field.DeclaringType); } else { switch (field.Type.Tag) { case TypeTag.AnyEnum: case TypeTag.S16: case TypeTag.S32: case TypeTag.S64: case TypeTag.S8: case TypeTag.U16: case TypeTag.U32: case TypeTag.U64: case TypeTag.U8: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); case TypeTag.AnyPointer: case TypeTag.CapabilityPointer: case TypeTag.Data: case TypeTag.Group: case TypeTag.Interface: case TypeTag.List: case TypeTag.ListPointer: case TypeTag.Struct: case TypeTag.StructPointer: case TypeTag.Text: return LiteralExpression(SyntaxKind.NullLiteralExpression); case TypeTag.Bool: return LiteralExpression(SyntaxKind.FalseLiteralExpression); case TypeTag.Enum: return CastExpression( _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant, Nullability.NonNullable), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))); case TypeTag.F32: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0f)); case TypeTag.F64: return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0)); default: throw new NotImplementedException(); } } } IEnumerable MakeUnionDiscriminatorSetter(TypeDefinition def) { var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); foreach (var unionField in unionFields) { var section = SwitchSection() .WithLabels( SingletonList( CaseSwitchLabel(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.GetCodeIdentifier(unionField).IdentifierName)))); if (unionField.Type.Tag != TypeTag.Void) { section = section.AddStatements( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.UnionContentField.IdentifierName, MakeDefaultValue(unionField)))); } section = section.AddStatements(BreakStatement()); yield return section; } } MemberDeclarationSyntax MakeUnionDiscriminatorProperty(TypeDefinition def) { return PropertyDeclaration(_names.UnionDiscriminatorEnum.IdentifierName, _names.UnionDiscriminatorProp.Identifier) .AddModifiers(Public).AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithExpressionBody( ArrowExpressionClause(_names.UnionDiscriminatorField.IdentifierName)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) .WithBody( Block( IfStatement( BinaryExpression( SyntaxKind.EqualsExpression, IdentifierName("value"), _names.UnionDiscriminatorField.IdentifierName), ReturnStatement()), ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.UnionDiscriminatorField.IdentifierName, IdentifierName("value"))), SwitchStatement(IdentifierName("value")) .WithOpenParenToken( Token(SyntaxKind.OpenParenToken)) .WithCloseParenToken( Token(SyntaxKind.CloseParenToken)) .AddSections(MakeUnionDiscriminatorSetter(def).ToArray())))); } MemberDeclarationSyntax MakeField(Field field) { if (field.DiscValue.HasValue) return MakeUnionField(field); else return MakeStructField(field); } ExpressionSyntax MakeListSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain) { string s = $"_s{type.GetRank().Item1}"; string v = $"_v{type.GetRank().Item1}"; switch (type.ElementType?.Tag) { case TypeTag.List: case TypeTag.ListPointer: case TypeTag.Struct: case TypeTag.Group: case TypeTag.StructPointer: case TypeTag.Data: return InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, writer, IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer.Init)))) .AddArgumentListArguments( Argument(domain), Argument( ParenthesizedLambdaExpression( MakeComplexSerializeParticle( type.ElementType, IdentifierName(s), IdentifierName(v))) .AddParameterListParameters( Parameter(Identifier(s)), Parameter(Identifier(v))))); default: return InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, writer, IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer.Init)))) .AddArgumentListArguments(Argument(domain)); } } ExpressionSyntax MakeComplexSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain) { switch (type.Tag) { case TypeTag.Data: case TypeTag.List: return MakeListSerializeParticle(type, writer, domain); case TypeTag.Struct: case TypeTag.Group: return ConditionalAccessExpression(domain, InvocationExpression(MemberBindingExpression(_names.SerializeMethod.IdentifierName)) .AddArgumentListArguments(Argument(writer))); default: throw new NotImplementedException(); } } ExpressionSyntax ConditionalSuppressNullableWarning(ExpressionSyntax expression, bool suppress) { return suppress ? _names.SuppressNullableWarning(expression) : expression; } StatementSyntax MakeSerializeMethodFieldAssignment(Field field) { var writerProp = MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.WriterParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName); switch (field.Type.Tag) { case TypeTag.Bool: case TypeTag.Enum: case TypeTag.F32: case TypeTag.F64: case TypeTag.S16: case TypeTag.S32: case TypeTag.S64: case TypeTag.S8: case TypeTag.U16: case TypeTag.U32: case TypeTag.U64: case TypeTag.U8: case TypeTag.AnyEnum: case TypeTag.List when field.Type.Tag == TypeTag.Void: if (field.DiscValue.HasValue) { return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, writerProp, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.SuppressNullableWarning(_names.GetCodeIdentifier(field).IdentifierName), IdentifierName(nameof(Nullable.Value))))); } else { return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, writerProp, _names.GetCodeIdentifier(field).IdentifierName)); } case TypeTag.AnyPointer: case TypeTag.ListPointer: case TypeTag.StructPointer: return ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ConditionalSuppressNullableWarning(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.WriterParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName), field.DiscValue.HasValue), IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject)))) .AddArgumentListArguments( Argument(_names.GetCodeIdentifier(field).IdentifierName))); case TypeTag.CapabilityPointer: case TypeTag.Interface: return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, writerProp, _names.GetCodeIdentifier(field).IdentifierName)); case TypeTag.Text: return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, writerProp, _names.GetCodeIdentifier(field).IdentifierName)); case TypeTag.Data: case TypeTag.List: case TypeTag.Struct: case TypeTag.Group: return ExpressionStatement( MakeComplexSerializeParticle( field.Type, field.DiscValue.HasValue ? _names.SuppressNullableWarning(writerProp) : writerProp, _names.GetCodeIdentifier(field).IdentifierName)); case TypeTag.Void: return null; default: throw new NotImplementedException(); } } StatementSyntax MakeApplyDefaultsMethodFieldAssignment(Field field) { var lhs = _names.GetCodeIdentifier(field).IdentifierName; var rhs = MakeDefaultValue(field); if (rhs == null) { return null; } return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, lhs, BinaryExpression(SyntaxKind.CoalesceExpression, lhs, rhs))); } ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType) { return ConditionalAccessExpression( context, InvocationExpression( MemberBindingExpression( IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) .AddArgumentListArguments(Argument( SimpleLambdaExpression(Parameter(Identifier("_")), InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(Capnp.CapnpSerializable)), GenericName(nameof(Capnp.CapnpSerializable.Create)) .AddTypeArgumentListArguments(MakeNonNullableType(elementType)))) .AddArgumentListArguments(Argument(IdentifierName("_"))))))); } ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank) { if (rank == 1) { return MakeInnerStructListConversion(context, elementType); } string lambdaVarName = $"_{rank}"; return ConditionalAccessExpression( context, InvocationExpression( MemberBindingExpression( IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) .AddArgumentListArguments(Argument( SimpleLambdaExpression( Parameter(Identifier(lambdaVarName)), MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1))))); } ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context) { return ConditionalAccessExpression( context, InvocationExpression( MemberBindingExpression( IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) .AddArgumentListArguments(Argument( SimpleLambdaExpression( Parameter(Identifier("_")), CastExpression(_names.Type(Nullability.NonNullable), IdentifierName("_")))))); } ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field) { switch (field.Type.Tag) { case TypeTag.Struct: case TypeTag.Group: case TypeTag.StructPointer: case TypeTag.AnyPointer: return InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(Capnp.CapnpSerializable)), GenericName(nameof(Capnp.CapnpSerializable.Create)) .AddTypeArgumentListArguments( MakeNonNullableType(_names.MakeTypeSyntax( field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NonNullable))))) .AddArgumentListArguments(Argument(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName))); case TypeTag.Void: return null; case TypeTag.List: (var rank, var elementType) = field.Type.GetRank(); if (elementType.Tag != TypeTag.Struct) break; return MakeStructListConversion( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName), _names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef), rank); case TypeTag.ListPointer: return MakeAnyListConversion( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName)); } return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName); } IEnumerable MakeSerializeMethodSwitchSections(TypeDefinition def) { var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); foreach (var unionField in unionFields) { var section = SwitchSection() .AddLabels( CaseSwitchLabel(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.GetCodeIdentifier(unionField).IdentifierName))); if (unionField.Type.Tag != TypeTag.Void) { section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField)); } section = section.AddStatements(BreakStatement()); yield return section; } } IEnumerable MakeDeserializeMethodSwitchSections(TypeDefinition def) { var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); foreach (var unionField in unionFields) { var section = SwitchSection() .AddLabels( CaseSwitchLabel(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.UnionDiscriminatorEnum.IdentifierName, _names.GetCodeIdentifier(unionField).IdentifierName))); switch (unionField.Type.Tag) { case TypeTag.Void: section = section.AddStatements( ExpressionStatement(AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.UnionDiscriminatorProp.IdentifierName, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.UnionDiscriminatorProp.IdentifierName)))); break; default: section = section.AddStatements( ExpressionStatement(AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.GetCodeIdentifier(unionField).IdentifierName, MakeDeserializeMethodRightHandSide(unionField)))); break; } section = section.AddStatements(BreakStatement()); yield return section; } } IEnumerable MakeSerializeStatements(TypeDefinition def) { if (def.UnionInfo != null) { yield return ExpressionStatement(AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.WriterParameter.IdentifierName, _names.UnionDiscriminatorProp.IdentifierName), _names.UnionDiscriminatorProp.IdentifierName)); yield return SwitchStatement(_names.UnionDiscriminatorProp.IdentifierName) .WithOpenParenToken(Token(SyntaxKind.OpenParenToken)) .WithCloseParenToken(Token(SyntaxKind.CloseParenToken)) .AddSections(MakeSerializeMethodSwitchSections(def).ToArray()); } var nondiscFields = def.Fields.Where(f => !f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void); foreach (var field in nondiscFields) { var asmt = MakeSerializeMethodFieldAssignment(field); if (asmt != null) { yield return asmt; } } } IEnumerable MakeApplyDefaultsStatements(TypeDefinition def) { var relevantFields = def.Fields.Where( f => !f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void && f.DefaultValueIsExplicit && !f.Type.IsValueType); foreach (var field in relevantFields) { var asmt = MakeApplyDefaultsMethodFieldAssignment(field); if (asmt != null) { yield return asmt; } } } MemberDeclarationSyntax MakeSerializeMethod(TypeDefinition def) { return MethodDeclaration(PredefinedType( Token(SyntaxKind.VoidKeyword)), _names.SerializeMethod.Identifier) .AddModifiers(Public) .AddParameterListParameters( Parameter(_names.WriterParameter.Identifier) .WithType(_names.WriterStruct.IdentifierName)) .AddBodyStatements(MakeSerializeStatements(def).ToArray()); } MemberDeclarationSyntax MakeSerializeInterfaceMethod() { return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(nameof(Capnp.ICapnpSerializable.Serialize))) .WithExplicitInterfaceSpecifier( ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) .AddParameterListParameters( Parameter(_names.AnonymousParameter.Identifier) .WithType(_names.Type(Nullability.NonNullable))) .AddBodyStatements( ExpressionStatement( InvocationExpression(_names.SerializeMethod.IdentifierName) .AddArgumentListArguments( Argument( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.AnonymousParameter.IdentifierName, GenericName(Identifier(nameof(Capnp.SerializerState.Rewrap))) .AddTypeArgumentListArguments(_names.WriterStruct.IdentifierName))))))); } MemberDeclarationSyntax MakeApplyDefaultsMethod(TypeDefinition def) { return MethodDeclaration(PredefinedType( Token(SyntaxKind.VoidKeyword)), _names.ApplyDefaultsMethod.Identifier) .AddModifiers(Public) .AddBodyStatements(MakeApplyDefaultsStatements(def).ToArray()); } IEnumerable MakeDeserializeStatements(TypeDefinition def) { var relevantFields = def.Fields.Where( f => !f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void); foreach (var field in relevantFields) { var rhs = MakeDeserializeMethodRightHandSide(field); if (rhs != null) { yield return ExpressionStatement(AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, _names.GetCodeIdentifier(field).IdentifierName, rhs)); } } } MemberDeclarationSyntax MakeDeserializeMethod(TypeDefinition def) { var stmts = new List(); stmts.Add(LocalDeclarationStatement( VariableDeclaration(IdentifierName("var")) .AddVariables( VariableDeclarator(_names.ReaderParameter.Identifier) .WithInitializer( EqualsValueClause( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderStruct.IdentifierName, _names.ReaderCreateMethod.IdentifierName)) .AddArgumentListArguments( Argument(_names.AnonymousParameter.IdentifierName))))))); if (def.UnionInfo != null) { stmts.Add(SwitchStatement( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.UnionDiscriminatorProp.IdentifierName)) .WithOpenParenToken(Token(SyntaxKind.OpenParenToken)) .WithCloseParenToken(Token(SyntaxKind.CloseParenToken)) .AddSections(MakeDeserializeMethodSwitchSections(def).ToArray())); } stmts.AddRange(MakeDeserializeStatements(def)); stmts.Add(ExpressionStatement(InvocationExpression( _names.ApplyDefaultsMethod.IdentifierName))); return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(nameof(Capnp.ICapnpSerializable.Deserialize))) .WithExplicitInterfaceSpecifier( ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) .AddParameterListParameters( Parameter(_names.AnonymousParameter.Identifier) .WithType(_names.Type(Nullability.NonNullable))) .AddBodyStatements(stmts.ToArray()); } IEnumerable EnumerateDomainClassMembers(TypeDefinition def) { yield return MakeDeserializeMethod(def); if (def.UnionInfo != null) { yield return MakeUnionDiscriminatorField(); if (def.Fields.Any(f => f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void)) { yield return MakeUnionContentField(); } yield return MakeUnionDiscriminatorProperty(def); } yield return MakeSerializeMethod(def); yield return MakeSerializeInterfaceMethod(); yield return MakeApplyDefaultsMethod(def); foreach (var field in def.Fields) { var decl = MakeField(field); if (decl != null) yield return decl; } } public MemberDeclarationSyntax[] MakeDomainClassMembers(TypeDefinition def) { return EnumerateDomainClassMembers(def).ToArray(); } } }