Ejemplo n.º 1
0
        private static List <SerializerFieldDescription> GetFieldDescriptions(TypeDescription typeDescription, LibraryTypes libraryTypes)
        {
            var type   = typeDescription.Type;
            var fields = new List <SerializerFieldDescription>();

            fields.AddRange(typeDescription.Members.Select(m => GetExpectedType(m.Type)).Distinct().Select(GetTypeDescription));

            if (HasComplexBaseType(type))
            {
                fields.Add(new InjectedFieldDescription(libraryTypes.PartialSerializer.Construct(type.BaseType), BaseTypeSerializerFieldName));
            }

            // Add a codec field for any field in the target which does not have a static codec.
            fields.AddRange(typeDescription.Members
                            .Select(m => GetExpectedType(m.Type)).Distinct()
                            .Where(t => !libraryTypes.StaticCodecs.Any(c => c.UnderlyingType.Equals(t)))
                            .Select(GetCodecDescription));
            return(fields);

            CodecFieldDescription GetCodecDescription(ITypeSymbol t)
            {
                var codecType = libraryTypes.FieldCodec.Construct(t);
                var fieldName = '_' + ToLowerCamelCase(t.GetValidIdentifier()) + "Codec";

                return(new CodecFieldDescription(codecType, fieldName, t));
            }

            TypeFieldDescription GetTypeDescription(ITypeSymbol t)
            {
                var fieldName = ToLowerCamelCase(t.GetValidIdentifier()) + "Type";

                return(new TypeFieldDescription(libraryTypes.Type, fieldName, t));
            }

            string ToLowerCamelCase(string input) => char.IsLower(input, 0) ? input : char.ToLowerInvariant(input[0]) + input.Substring(1);
        }
Ejemplo n.º 2
0
        private static MemberDeclarationSyntax GenerateDeserializeMethod(TypeDescription typeDescription,
                                                                         List <SerializerFieldDescription> fieldDescriptions,
                                                                         LibraryTypes libraryTypes)
        {
            var returnType = PredefinedType(Token(SyntaxKind.VoidKeyword));

            var readerParam   = "reader".ToIdentifierName();
            var sessionParam  = "session".ToIdentifierName();
            var instanceParam = "instance".ToIdentifierName();
            var fieldIdVar    = "fieldId".ToIdentifierName();
            var headerVar     = "header".ToIdentifierName();

            var body = new List <StatementSyntax>
            {
                // C#: uint fieldId = 0;
                LocalDeclarationStatement(
                    VariableDeclaration(
                        PredefinedType(Token(SyntaxKind.UIntKeyword)),
                        SingletonSeparatedList(VariableDeclarator(fieldIdVar.Identifier)
                                               .WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)))))))
            };

            if (HasComplexBaseType(typeDescription.Type))
            {
                // C#: this.baseTypeSerializer.Deserialize(ref reader, session, instance);
                body.Add(
                    ExpressionStatement(
                        InvocationExpression(
                            ThisExpression().Member(BaseTypeSerializerFieldName.ToIdentifierName()).Member(DeserializeMethodName),
                            ArgumentList(SeparatedList(new[]
                {
                    Argument(readerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
                    Argument(sessionParam),
                    Argument(instanceParam)
                })))));
            }

            body.Add(WhileStatement(LiteralExpression(SyntaxKind.TrueLiteralExpression), Block(GetDeserializerLoopBody())));

            var parameters = new[]
            {
                Parameter(readerParam.Identifier).WithType(libraryTypes.Reader.ToTypeSyntax()).WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))),
                Parameter(sessionParam.Identifier).WithType(libraryTypes.SerializerSession.ToTypeSyntax()),
                Parameter(instanceParam.Identifier).WithType(typeDescription.Type.ToTypeSyntax())
            };

            if (typeDescription.Type.IsValueType)
            {
                parameters[2] = parameters[2].WithModifiers(TokenList(Token(SyntaxKind.RefKeyword)));
            }

            return(MethodDeclaration(returnType, DeserializeMethodName)
                   .AddModifiers(Token(SyntaxKind.PublicKeyword))
                   .AddParameterListParameters(parameters)
                   .AddBodyStatements(body.ToArray()));

            // Create the loop body.
            List <StatementSyntax> GetDeserializerLoopBody()
            {
                return(new List <StatementSyntax>
                {
                    // C#: var header = reader.ReadFieldHeader(session);
                    LocalDeclarationStatement(
                        VariableDeclaration(
                            IdentifierName("var"),
                            SingletonSeparatedList(
                                VariableDeclarator(headerVar.Identifier)
                                .WithInitializer(EqualsValueClause(InvocationExpression(readerParam.Member("ReadFieldHeader"),
                                                                                        ArgumentList(SingletonSeparatedList(Argument(sessionParam))))))))),

                    // C#: if (header.IsEndBaseOrEndObject) break;
                    IfStatement(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, headerVar, IdentifierName("IsEndBaseOrEndObject")), BreakStatement()),

                    // C#: fieldId += header.FieldIdDelta;
                    ExpressionStatement(
                        AssignmentExpression(
                            SyntaxKind.AddAssignmentExpression,
                            fieldIdVar,
                            Token(SyntaxKind.PlusEqualsToken),
                            MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, headerVar, IdentifierName("FieldIdDelta")))),

                    // C#: switch (fieldId) { ... }
                    SwitchStatement(fieldIdVar, List(GetSwitchSections()))
                });
            }

            // Creates switch sections for each member.
            List <SwitchSectionSyntax> GetSwitchSections()
            {
                var switchSections = new List <SwitchSectionSyntax>();

                foreach (var member in typeDescription.Members)
                {
                    // C#: case <fieldId>:
                    var label = CaseSwitchLabel(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(member.FieldId)));

                    // C#: instance.<member> = this.<codec>.ReadValue(ref reader, session, header);
                    var codec = fieldDescriptions.OfType <ICodecDescription>()
                                .Concat(libraryTypes.StaticCodecs)
                                .First(f => f.UnderlyingType.Equals(GetExpectedType(member.Type)));

                    // Codecs can either be static classes or injected into the constructor.
                    // Either way, the member signatures are the same.
                    var memberType  = GetExpectedType(member.Type);
                    var staticCodec = libraryTypes.StaticCodecs.FirstOrDefault(c => c.UnderlyingType.Equals(memberType));
                    ExpressionSyntax codecExpression;
                    if (staticCodec != null)
                    {
                        codecExpression = staticCodec.CodecType.ToNameSyntax();
                    }
                    else
                    {
                        var instanceCodec = fieldDescriptions.OfType <CodecFieldDescription>().First(f => f.UnderlyingType.Equals(memberType));
                        codecExpression = ThisExpression().Member(instanceCodec.FieldName);
                    }

                    ExpressionSyntax readValueExpression = InvocationExpression(
                        codecExpression.Member("ReadValue"),
                        ArgumentList(SeparatedList(new[] { Argument(readerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(sessionParam), Argument(headerVar) })));
                    if (!codec.UnderlyingType.Equals(member.Type))
                    {
                        // If the member type type differs from the codec type (eg because the member is an array), cast the result.
                        readValueExpression = CastExpression(member.Type.ToTypeSyntax(), readValueExpression);
                    }

                    var memberAssignment =
                        ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, instanceParam.Member(member.Member.Name), readValueExpression));
                    var caseBody = List(new StatementSyntax[] { memberAssignment, BreakStatement() });

                    // Create the switch section with a break at the end.
                    // C#: break;
                    switchSections.Add(SwitchSection(SingletonList <SwitchLabelSyntax>(label), caseBody));
                }

                // Add the default switch section.
                var consumeUnknown = ExpressionStatement(InvocationExpression(readerParam.Member("ConsumeUnknownField"),
                                                                              ArgumentList(SeparatedList(new[] { Argument(sessionParam), Argument(headerVar) }))));

                switchSections.Add(SwitchSection(SingletonList <SwitchLabelSyntax>(DefaultSwitchLabel()), List(new StatementSyntax[] { consumeUnknown, BreakStatement() })));

                return(switchSections);
            }
        }
Ejemplo n.º 3
0
        private static MemberDeclarationSyntax GenerateSerializeMethod(
            TypeDescription typeDescription,
            List <SerializerFieldDescription> fieldDescriptions,
            LibraryTypes libraryTypes)
        {
            var returnType = PredefinedType(Token(SyntaxKind.VoidKeyword));

            var writerParam   = "writer".ToIdentifierName();
            var sessionParam  = "session".ToIdentifierName();
            var instanceParam = "instance".ToIdentifierName();

            var body = new List <StatementSyntax>();

            if (HasComplexBaseType(typeDescription.Type))
            {
                body.Add(
                    ExpressionStatement(
                        InvocationExpression(
                            ThisExpression().Member(BaseTypeSerializerFieldName.ToIdentifierName()).Member(SerializeMethodName),
                            ArgumentList(SeparatedList(new[] { Argument(writerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(sessionParam), Argument(instanceParam) })))));
                body.Add(ExpressionStatement(InvocationExpression(writerParam.Member("WriteEndBase"), ArgumentList())));
            }

            // Order members according to their FieldId, since fields must be serialized in order and FieldIds are serialized as deltas.
            uint previousFieldId = 0;

            foreach (var member in typeDescription.Members.OrderBy(m => m.FieldId))
            {
                var fieldIdDelta = member.FieldId - previousFieldId;
                previousFieldId = member.FieldId;

                // Codecs can either be static classes or injected into the constructor.
                // Either way, the member signatures are the same.
                var memberType  = GetExpectedType(member.Type);
                var staticCodec = libraryTypes.StaticCodecs.FirstOrDefault(c => c.UnderlyingType.Equals(memberType));
                ExpressionSyntax codecExpression;
                if (staticCodec != null)
                {
                    codecExpression = staticCodec.CodecType.ToNameSyntax();
                }
                else
                {
                    var instanceCodec = fieldDescriptions.OfType <CodecFieldDescription>().First(f => f.UnderlyingType.Equals(memberType));
                    codecExpression = ThisExpression().Member(instanceCodec.FieldName);
                }

                var expectedType = fieldDescriptions.OfType <TypeFieldDescription>().First(f => f.UnderlyingType.Equals(memberType));
                body.Add(
                    ExpressionStatement(
                        InvocationExpression(
                            codecExpression.Member("WriteField"),
                            ArgumentList(
                                SeparatedList(
                                    new[]
                {
                    Argument(writerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
                    Argument(sessionParam),
                    Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(fieldIdDelta))),
                    Argument(expectedType.FieldName.ToIdentifierName()),
                    Argument(instanceParam.Member(member.Member.Name))
                })))));
            }

            var parameters = new[]
            {
                Parameter("writer".ToIdentifier()).WithType(libraryTypes.Writer.ToTypeSyntax()).WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))),
                Parameter("session".ToIdentifier()).WithType(libraryTypes.SerializerSession.ToTypeSyntax()),
                Parameter("instance".ToIdentifier()).WithType(typeDescription.Type.ToTypeSyntax())
            };

            if (typeDescription.Type.IsValueType)
            {
                parameters[2] = parameters[2].WithModifiers(TokenList(Token(SyntaxKind.RefKeyword)));
            }

            return(MethodDeclaration(returnType, SerializeMethodName)
                   .AddModifiers(Token(SyntaxKind.PublicKeyword))
                   .AddParameterListParameters(parameters)
                   .AddBodyStatements(body.ToArray()));
        }
Ejemplo n.º 4
0
        public static ClassDeclarationSyntax GenerateSerializer(Compilation compilation, TypeDescription typeDescription)
        {
            var type            = typeDescription.Type;
            var simpleClassName = GetSimpleClassName(type);

            var libraryTypes        = LibraryTypes.FromCompilation(compilation);
            var serializerInterface = type.IsValueType ? libraryTypes.ValueSerializer : libraryTypes.PartialSerializer;
            var baseInterface       = serializerInterface.Construct(type).ToTypeSyntax();

            var fieldDescriptions = GetFieldDescriptions(typeDescription, libraryTypes);
            var fields            = GetFieldDeclarations(fieldDescriptions);
            var ctor = GenerateConstructor(simpleClassName, fieldDescriptions);

            var serializeMethod   = GenerateSerializeMethod(typeDescription, fieldDescriptions, libraryTypes);
            var deserializeMethod = GenerateDeserializeMethod(typeDescription, fieldDescriptions, libraryTypes);

            var classDeclaration = ClassDeclaration(simpleClassName)
                                   .AddBaseListTypes(SimpleBaseType(baseInterface))
                                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                                   .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                                   .AddMembers(fields)
                                   .AddMembers(ctor, serializeMethod, deserializeMethod);

            if (type.IsGenericType)
            {
                classDeclaration = AddGenericTypeConstraints(classDeclaration, type);
            }

            return(classDeclaration);
        }