예제 #1
0
        public static (ClassDeclarationSyntax, IGeneratedProxyDescription) Generate(
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel)
        {
            var generatedClassName = GetSimpleClassName(interfaceDescription.InterfaceType);

            var ctors        = GenerateConstructors(generatedClassName, interfaceDescription).ToArray();
            var proxyMethods = CreateProxyMethods(libraryTypes, interfaceDescription, metadataModel).ToArray();

            var classDeclaration = ClassDeclaration(generatedClassName)
                                   .AddBaseListTypes(
                SimpleBaseType(interfaceDescription.ProxyBaseType.ToTypeSyntax()),
                SimpleBaseType(interfaceDescription.InterfaceType.ToTypeSyntax()))
                                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                                   .AddAttributeLists(
                AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                                   .AddMembers(ctors)
                                   .AddMembers(proxyMethods);

            if (interfaceDescription.InterfaceType.TypeParameters.Length > 0)
            {
                classDeclaration = AddGenericTypeConstraints(classDeclaration, interfaceDescription.InterfaceType);
            }

            return(classDeclaration, new GeneratedProxyDescription(interfaceDescription));
        }
예제 #2
0
        private static MemberDeclarationSyntax GenerateGetResultProperty(
            LibraryTypes libraryTypes,
            ResultFieldDescription resultField)
        {
            var type      = IdentifierName("TResult");
            var typeToken = Identifier("TResult");

            ExpressionSyntax body;

            if (resultField != null)
            {
                body = CastExpression(
                    type,
                    CastExpression(libraryTypes.Object.ToTypeSyntax(), ThisExpression().Member(resultField.FieldName)));
            }
            else
            {
                body = ThrowExpression(
                    ObjectCreationExpression(libraryTypes.InvalidOperationException.ToTypeSyntax())
                    .WithArgumentList(
                        ArgumentList(
                            SingletonSeparatedList(
                                Argument("Method does not have a return value.".GetLiteralExpression())))));
            }

            return(MethodDeclaration(type, "GetResult")
                   .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter(typeToken))))
                   .WithParameterList(ParameterList())
                   .WithExpressionBody(ArrowExpressionClause(body))
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
        }
예제 #3
0
        private static MemberDeclarationSyntax GenerateDisposeMethod(
            LibraryTypes libraryTypes,
            List <FieldDescription> fields)
        {
            var body = new List <StatementSyntax>();

            foreach (var field in fields)
            {
                if (!field.IsInjected)
                {
                    body.Add(
                        ExpressionStatement(
                            AssignmentExpression(
                                SyntaxKind.SimpleAssignmentExpression,
                                ThisExpression().Member(field.FieldName),
                                DefaultExpression(field.FieldType.ToTypeSyntax()))));
                }
            }

            body.Add(ExpressionStatement(InvocationExpression(libraryTypes.InvokablePool.ToTypeSyntax().Member("Return"))
                                         .WithArgumentList(ArgumentList(SingletonSeparatedList <ArgumentSyntax>(Argument(ThisExpression()))))));

            return(MethodDeclaration(libraryTypes.Void.ToTypeSyntax(), "Dispose")
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
                   .WithBody(Block(body)));
        }
예제 #4
0
 public SerializableMethodMember(LibraryTypes wellKnownTypes, MethodParameterFieldDescription member, int ordinal)
 {
     this.member         = member;
     this.wellKnownTypes = wellKnownTypes;
     this.Description    = member;
     this.ordinal        = ordinal;
 }
예제 #5
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);
        }
예제 #6
0
        private static MemberDeclarationSyntax GenerateResultProperty(
            LibraryTypes libraryTypes,
            ResultFieldDescription resultField)
        {
            var getter = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                         .WithExpressionBody(
                ArrowExpressionClause(
                    CastExpression(
                        libraryTypes.Object.ToTypeSyntax(),
                        ThisExpression().Member(resultField.FieldName))))
                         .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));

            var setter = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                         .WithExpressionBody(
                ArrowExpressionClause(
                    AssignmentExpression(
                        SyntaxKind.SimpleAssignmentExpression,
                        ThisExpression().Member(resultField.FieldName),
                        CastExpression(resultField.FieldType.ToTypeSyntax(), IdentifierName("value")))))
                         .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));

            return(PropertyDeclaration(libraryTypes.Object.ToTypeSyntax(), "Result")
                   .WithAccessorList(
                       AccessorList()
                       .AddAccessors(
                           getter,
                           setter))
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword))));
        }
예제 #7
0
 public SerializableTypeDescription(SemanticModel semanticModel, INamedTypeSymbol type, IEnumerable <IMemberDescription> members, LibraryTypes libraryTypes)
 {
     Type          = type;
     Members       = members.ToList();
     SemanticModel = semanticModel;
     _libraryTypes = libraryTypes;
 }
예제 #8
0
 public SerializableMember(LibraryTypes wellKnownTypes, ISerializableTypeDescription type, IMemberDescription member, int ordinal)
 {
     this.wellKnownTypes = wellKnownTypes;
     this.model          = type.SemanticModel;
     this.Description    = member;
     this.ordinal        = ordinal;
 }
예제 #9
0
        private static MemberDeclarationSyntax GenerateSetTargetMethod(
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription interfaceDescription,
            TargetFieldDescription targetField)
        {
            var type            = IdentifierName("TTargetHolder");
            var typeToken       = Identifier("TTargetHolder");
            var holderParameter = Identifier("holder");
            var holder          = IdentifierName("holder");

            var getTarget = InvocationExpression(
                MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    holder,
                    GenericName(interfaceDescription.IsExtension ? "GetExtension" : "GetTarget")
                    .WithTypeArgumentList(
                        TypeArgumentList(
                            SingletonSeparatedList(interfaceDescription.InterfaceType.ToTypeSyntax())))))
                            .WithArgumentList(ArgumentList());

            var body =
                AssignmentExpression(
                    SyntaxKind.SimpleAssignmentExpression,
                    ThisExpression().Member(targetField.FieldName),
                    getTarget);

            return(MethodDeclaration(libraryTypes.Void.ToTypeSyntax(), "SetTarget")
                   .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter(typeToken))))
                   .WithParameterList(ParameterList(SingletonSeparatedList(Parameter(holderParameter).WithType(type))))
                   .WithExpressionBody(ArrowExpressionClause(body))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword))));
        }
예제 #10
0
        private static MemberDeclarationSyntax GenerateCreateMethod(LibraryTypes libraryTypes, ISerializableTypeDescription type)
        {
            var createObject = type.GetObjectCreationExpression(libraryTypes);

            return(MethodDeclaration(type.TypeSyntax, "Create")
                   .WithExpressionBody(ArrowExpressionClause(createObject))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
                   .AddModifiers(Token(SyntaxKind.PublicKeyword)));
        }
예제 #11
0
        private static BlockSyntax CreateProxyMethodBody(
            LibraryTypes libraryTypes,
            MetadataModel metadataModel,
            IInvokableInterfaceDescription interfaceDescription,
            MethodDescription methodDescription)
        {
            var statements = new List <StatementSyntax>();

            // Create request object
            var requestVar = IdentifierName("request");

            var requestDescription = metadataModel.GeneratedInvokables[methodDescription];
            var createRequestExpr  = ObjectCreationExpression(requestDescription.TypeSyntax)
                                     .WithArgumentList(ArgumentList(SeparatedList <ArgumentSyntax>()));

            statements.Add(
                LocalDeclarationStatement(
                    VariableDeclaration(
                        ParseTypeName("var"),
                        SingletonSeparatedList(
                            VariableDeclarator(
                                Identifier("request"))
                            .WithInitializer(
                                EqualsValueClause(createRequestExpr))))));

            // Set request object fields from method parameters.
            var parameterIndex = 0;

            foreach (var parameter in methodDescription.Method.Parameters)
            {
                statements.Add(
                    ExpressionStatement(
                        AssignmentExpression(
                            SyntaxKind.SimpleAssignmentExpression,
                            requestVar.Member($"arg{parameterIndex}"),
                            IdentifierName(parameter.Name))));

                parameterIndex++;
            }

            // Issue request
            statements.Add(
                ExpressionStatement(
                    AwaitExpression(
                        InvocationExpression(
                            BaseExpression().Member("Invoke"),
                            ArgumentList(SingletonSeparatedList(Argument(requestVar)))))));

            // Return result
            if (methodDescription.Method.ReturnType is INamedTypeSymbol named && named.TypeParameters.Length == 1)
            {
                statements.Add(ReturnStatement(requestVar.Member("result")));
            }

            return(Block(statements));
        }
예제 #12
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), 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;

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

            return(MethodDeclaration(returnType, SerializeMethodName)
                   .AddModifiers(Token(SyntaxKind.PublicKeyword))
                   .AddParameterListParameters(
                       Parameter("writer".ToIdentifier()).WithType(libraryTypes.Writer.ToTypeSyntax()),
                       Parameter("session".ToIdentifier()).WithType(libraryTypes.SerializerSession.ToTypeSyntax()),
                       Parameter("instance".ToIdentifier()).WithType(typeDescription.Type.ToTypeSyntax()))
                   .AddBodyStatements(body.ToArray()));
        }
예제 #13
0
 public CodeGenerator(Compilation compilation, CodeGeneratorOptions options)
 {
     this.compilation  = compilation;
     this.options      = options;
     this.libraryTypes = LibraryTypes.FromCompilation(compilation, options);
     if (options.GenerateSerializerAttributes != null)
     {
         this.generateSerializerAttributes = options.GenerateSerializerAttributes.Select(compilation.GetTypeByMetadataName).ToArray();
     }
 }
예제 #14
0
 private static MemberDeclarationSyntax GenerateGetArgumentCount(
     LibraryTypes libraryTypes,
     MethodDescription methodDescription) =>
 PropertyDeclaration(libraryTypes.Int32.ToTypeSyntax(), "ArgumentCount")
 .WithExpressionBody(
     ArrowExpressionClause(
         LiteralExpression(
             SyntaxKind.NumericLiteralExpression,
             Literal(methodDescription.Method.Parameters.Length))))
 .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
 .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
예제 #15
0
        public static (ClassDeclarationSyntax, IGeneratedInvokerDescription) Generate(
            Compilation compilation,
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription interfaceDescription,
            MethodDescription methodDescription)
        {
            var method             = methodDescription.Method;
            var generatedClassName = GetSimpleClassName(method);

            var fieldDescriptions = GetFieldDescriptions(methodDescription.Method, interfaceDescription);
            var fields            = GetFieldDeclarations(fieldDescriptions, libraryTypes);
            var ctor = GenerateConstructor(generatedClassName, fieldDescriptions);

            var targetField = fieldDescriptions.OfType <TargetFieldDescription>().Single();
            var resultField = fieldDescriptions.OfType <ResultFieldDescription>().FirstOrDefault();

            var classDeclaration = ClassDeclaration(generatedClassName)
                                   .AddBaseListTypes(SimpleBaseType(libraryTypes.Invokable.ToTypeSyntax()))
                                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                                   .AddAttributeLists(
                AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                                   .AddMembers(fields)
                                   .AddMembers(ctor)
                                   .AddMembers(
                GenerateGetArgumentCount(libraryTypes, methodDescription),
                GenerateSetTargetMethod(libraryTypes, interfaceDescription, targetField),
                GenerateGetTargetMethod(libraryTypes, targetField),
                GenerateResetMethod(libraryTypes, fieldDescriptions),
                GenerateGetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions),
                GenerateSetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions),
                GenerateInvokeMethod(libraryTypes, methodDescription, fieldDescriptions, targetField, resultField),
                GenerateSetResultProperty(libraryTypes, resultField),
                GenerateGetResultProperty(libraryTypes, resultField));

            var typeParameters = interfaceDescription.InterfaceType.TypeParameters.Select(tp => (tp, tp.Name))
                                 .Concat(method.TypeParameters.Select(tp => (tp, tp.Name)))
                                 .ToList();

            if (typeParameters.Count > 0)
            {
                classDeclaration = AddGenericTypeConstraints(classDeclaration, typeParameters);
            }

            return(classDeclaration,
                   new GeneratedInvokerDescription(
                       interfaceDescription,
                       methodDescription,
                       generatedClassName,
                       fieldDescriptions.OfType <IMemberDescription>().ToList()));
        }
예제 #16
0
 public InvokableInterfaceDescription(
     LibraryTypes libraryTypes,
     SemanticModel semanticModel,
     INamedTypeSymbol interfaceType,
     IEnumerable <MethodDescription> methods,
     INamedTypeSymbol proxyBaseType,
     bool isExtension)
 {
     this.ValidateBaseClass(libraryTypes, proxyBaseType);
     this.SemanticModel = semanticModel;
     this.InterfaceType = interfaceType;
     this.ProxyBaseType = proxyBaseType;
     this.IsExtension   = isExtension;
     this.Methods       = methods.ToList();
 }
예제 #17
0
        private static MemberDeclarationSyntax GenerateGetTargetMethod(
            LibraryTypes libraryTypes,
            TargetFieldDescription targetField)
        {
            var type      = IdentifierName("TTarget");
            var typeToken = Identifier("TTarget");

            var body = CastExpression(type, ThisExpression().Member(targetField.FieldName));

            return(MethodDeclaration(type, "GetTarget")
                   .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter(typeToken))))
                   .WithParameterList(ParameterList())
                   .WithExpressionBody(ArrowExpressionClause(body))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword))));
        }
        private static void ValidateBaseClass(LibraryTypes l, INamedTypeSymbol baseClass)
        {
            var found = false;

            foreach (var member in baseClass.GetMembers("SendRequest"))
            {
                if (member is not IMethodSymbol method)
                {
                    Throw(member, "not method");
                }

                if (method.TypeParameters.Length != 0)
                {
                    Throw(member, "type params");
                }

                if (method.Parameters.Length != 2)
                {
                    Throw(member, "params length");
                }

                if (!SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, l.IResponseCompletionSource))
                {
                    Throw(member, "param 0");
                }

                if (!SymbolEqualityComparer.Default.Equals(method.Parameters[1].Type, l.IInvokable))
                {
                    Throw(member, "param 1");
                }

                if (!method.ReturnsVoid)
                {
                    Throw(member, "return type");
                }

                found = true;
            }

            if (!found)
            {
                throw new InvalidOperationException(
                          $"Proxy base class {baseClass} does not contain a definition for void SendRequest(IResponseCompletionSource, IInvokable)");
            }
예제 #19
0
        public static ClassDeclarationSyntax GenerateActivator(LibraryTypes libraryTypes, ISerializableTypeDescription type)
        {
            var simpleClassName = GetSimpleClassName(type);

            var baseInterface = libraryTypes.IActivator_1.ToTypeSyntax(type.TypeSyntax);

            var classDeclaration = ClassDeclaration(simpleClassName)
                                   .AddBaseListTypes(SimpleBaseType(baseInterface))
                                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                                   .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                                   .AddMembers(GenerateCreateMethod(libraryTypes, type));

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

            return(classDeclaration);
        }
예제 #20
0
        public static ClassDeclarationSyntax GenerateMetadata(Compilation compilation, List <TypeDescription> serializableTypes)
        {
            var configParam = "config".ToIdentifierName();
            var addMethod   = configParam.Member("PartialSerializers").Member("Add");
            var body        = new List <StatementSyntax>();

            body.AddRange(
                serializableTypes.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(InvocationExpression(addMethod, ArgumentList(SingletonSeparatedList(Argument(TypeOfExpression(GetPartialSerializerTypeName(type.Type)))))))
                    ));

            var libraryTypes    = LibraryTypes.FromCompilation(compilation);
            var configType      = libraryTypes.SerializerConfiguration;
            var configureMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "Configure")
                                  .AddModifiers(Token(SyntaxKind.PublicKeyword))
                                  .AddParameterListParameters(
                Parameter(configParam.Identifier).WithType(configType.ToTypeSyntax()))
                                  .AddBodyStatements(body.ToArray());

            var interfaceType = libraryTypes.ConfigurationProvider.Construct(configType);

            return(ClassDeclaration(CodeGenerator.CodeGeneratorName + "_Metadata_" + compilation.AssemblyName.Replace('.', '_'))
                   .AddBaseListTypes(SimpleBaseType(interfaceType.ToTypeSyntax()))
                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                   .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                   .AddMembers(configureMethod));

            TypeSyntax GetPartialSerializerTypeName(INamedTypeSymbol type)
            {
                var genericArity = type.TypeParameters.Length;
                var name         = PartialSerializerGenerator.GetSimpleClassName(type);

                if (genericArity > 0)
                {
                    name += $"<{new string(',', genericArity - 1)}>";
                }

                return(ParseTypeName(name));
            }
        }
예제 #21
0
        void ValidateBaseClass(LibraryTypes l, INamedTypeSymbol baseClass)
        {
            var found = false;

            foreach (var member in baseClass.GetMembers("SendRequest"))
            {
                if (!(member is IMethodSymbol method))
                {
                    Throw(member, "not method");
                }
                if (method.TypeParameters.Length != 0)
                {
                    Throw(member, "type params");
                }
                if (method.Parameters.Length != 2)
                {
                    Throw(member, "params length");
                }
                if (!method.Parameters[0].Type.Equals(l.IResponseCompletionSource))
                {
                    Throw(member, "param 0");
                }
                if (!method.Parameters[1].Type.Equals(l.IInvokable))
                {
                    Throw(member, "param 1");
                }
                if (!method.ReturnsVoid)
                {
                    Throw(member, "return type");
                }
                found = true;
            }

            if (!found)
            {
                throw new InvalidOperationException(
                          $"Proxy base class {baseClass} does not contain a definition for void SendRequest(IResponseCompletionSource, IInvokable)");
            }

            void Throw(ISymbol m, string x) => throw new InvalidOperationException("Complaint: " + x + " for symbol: " + m.ToDisplayString());
        }
예제 #22
0
        private static IEnumerable <MemberDeclarationSyntax> CreateProxyMethods(
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel)
        {
            foreach (var methodDescription in interfaceDescription.Methods)
            {
                yield return(CreateProxyMethod(methodDescription));
            }

            MethodDeclarationSyntax CreateProxyMethod(MethodDescription methodDescription)
            {
                var method      = methodDescription.Method;
                var declaration = MethodDeclaration(method.ReturnType.ToTypeSyntax(), method.Name)
                                  .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.AsyncKeyword))
                                  .AddParameterListParameters(method.Parameters.Select(GetParameterSyntax).ToArray())
                                  .WithBody(
                    CreateProxyMethodBody(libraryTypes, metadataModel, interfaceDescription, methodDescription));

                var typeParameters = GetTypeParametersWithConstraints(method.TypeParameters);

                foreach (var(name, constraints) in typeParameters)
                {
                    if (constraints.Count > 0)
                    {
                        declaration = declaration.AddConstraintClauses(
                            TypeParameterConstraintClause(name).AddConstraints(constraints.ToArray()));
                    }
                }

                if (typeParameters.Count > 0)
                {
                    declaration = declaration.WithTypeParameterList(
                        TypeParameterList(SeparatedList(typeParameters.Select(tp => TypeParameter(tp.Item1)))));
                }

                return(declaration);
            }
        }
예제 #23
0
        void ValidateBaseClass(LibraryTypes l, INamedTypeSymbol baseClass)
        {
            var found = false;

            foreach (var member in baseClass.GetMembers("Invoke"))
            {
                if (!(member is IMethodSymbol method))
                {
                    continue;
                }
                if (method.TypeParameters.Length != 1)
                {
                    continue;
                }
                if (method.Parameters.Length != 1)
                {
                    continue;
                }
                if (!method.Parameters[0].Type.Equals(method.TypeParameters[0]))
                {
                    continue;
                }
                if (!method.TypeParameters[0].ConstraintTypes.Contains(l.IInvokable))
                {
                    continue;
                }
                if (!method.ReturnType.Equals(l.ValueTask))
                {
                    continue;
                }
                found = true;
            }

            if (!found)
            {
                throw new InvalidOperationException(
                          $"Proxy base class {baseClass} does not contain a definition for ValueTask Invoke<T>(T) where T : IInvokable");
            }
        }
예제 #24
0
        private static MemberDeclarationSyntax GenerateResetMethod(
            LibraryTypes libraryTypes,
            List <FieldDescription> fields)
        {
            var body = new List <StatementSyntax>();

            foreach (var field in fields)
            {
                if (!field.IsInjected)
                {
                    body.Add(
                        ExpressionStatement(
                            AssignmentExpression(
                                SyntaxKind.SimpleAssignmentExpression,
                                ThisExpression().Member(field.FieldName),
                                DefaultExpression(field.FieldType.ToTypeSyntax()))));
                }
            }

            return(MethodDeclaration(libraryTypes.Void.ToTypeSyntax(), "Reset")
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
                   .WithBody(Block(body)));
        }
예제 #25
0
        private static MemberDeclarationSyntax GenerateInvokeInnerMethod(
            LibraryTypes libraryTypes,
            MethodDescription method,
            List <FieldDescription> fields,
            TargetFieldDescription target)
        {
            var resultTask = IdentifierName("resultTask");

            // C# var resultTask = this.target.{Method}({params});
            var args = SeparatedList(
                fields.OfType <MethodParameterFieldDescription>()
                .OrderBy(p => p.ParameterOrdinal)
                .Select(p => Argument(ThisExpression().Member(p.FieldName))));
            ExpressionSyntax methodCall;

            if (method.Method.TypeParameters.Length > 0)
            {
                methodCall = MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    ThisExpression().Member(target.FieldName),
                    GenericName(
                        Identifier(method.Method.Name),
                        TypeArgumentList(
                            SeparatedList <TypeSyntax>(
                                method.Method.TypeParameters.Select(p => IdentifierName(p.Name))))));
            }
            else
            {
                methodCall = ThisExpression().Member(target.FieldName).Member(method.Method.Name);
            }

            return(MethodDeclaration(method.Method.ReturnType.ToTypeSyntax(), "InvokeInner")
                   .WithParameterList(ParameterList())
                   .WithExpressionBody(ArrowExpressionClause(InvocationExpression(methodCall, ArgumentList(args))))
                   .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
                   .WithModifiers(TokenList(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword))));
        }
예제 #26
0
 public ExpressionSyntax GetObjectCreationExpression(LibraryTypes libraryTypes) => InvocationExpression(ObjectCreationExpression(this.TypeSyntax));
예제 #27
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);
            }
        }
예제 #28
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()));
        }
예제 #29
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);
        }
예제 #30
0
        private static MemberDeclarationSyntax GenerateInvokeMethod(
            LibraryTypes libraryTypes,
            MethodDescription method,
            List <FieldDescription> fields,
            TargetFieldDescription target,
            ResultFieldDescription result)
        {
            var body = new List <StatementSyntax>();

            var resultTask = IdentifierName("resultTask");

            // C# var resultTask = this.target.{Method}({params});
            var args = SeparatedList(
                fields.OfType <MethodParameterFieldDescription>()
                .OrderBy(p => p.ParameterOrdinal)
                .Select(p => Argument(ThisExpression().Member(p.FieldName))));
            ExpressionSyntax methodCall;

            if (method.Method.TypeParameters.Length > 0)
            {
                methodCall = MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    ThisExpression().Member(target.FieldName),
                    GenericName(
                        Identifier(method.Method.Name),
                        TypeArgumentList(
                            SeparatedList <TypeSyntax>(
                                method.Method.TypeParameters.Select(p => IdentifierName(p.Name))))));
            }
            else
            {
                methodCall = ThisExpression().Member(target.FieldName).Member(method.Method.Name);
            }

            body.Add(
                LocalDeclarationStatement(
                    VariableDeclaration(
                        ParseTypeName("var"),
                        SingletonSeparatedList(
                            VariableDeclarator(resultTask.Identifier)
                            .WithInitializer(
                                EqualsValueClause(
                                    InvocationExpression(
                                        methodCall,
                                        ArgumentList(args))))))));

            // C#: if (resultTask.IsCompleted) // Even if it failed.
            // C#: {
            // C#:     this.result = resultTask.GetAwaiter().GetResult();
            // C#:     return default; // default(ValueTask) is a successfully completed ValueTask.
            // C#: }
            var synchronousCompletionBody = new List <StatementSyntax>();

            if (result != null)
            {
                synchronousCompletionBody.Add(
                    ExpressionStatement(
                        AssignmentExpression(
                            SyntaxKind.SimpleAssignmentExpression,
                            ThisExpression().Member(result.FieldName),
                            InvocationExpression(
                                InvocationExpression(resultTask.Member("GetAwaiter")).Member("GetResult")))));
            }

            synchronousCompletionBody.Add(ReturnStatement(DefaultExpression(libraryTypes.ValueTask.ToTypeSyntax())));
            body.Add(IfStatement(resultTask.Member("IsCompleted"), Block(synchronousCompletionBody)));

            // C#: async ValueTask InvokeAsync(ValueTask<int> asyncValue)
            // C#: {
            // C#:     this.result = await asyncValue.ConfigureAwait(false);
            // C#: }
            var invokeAsyncParam = IdentifierName("asyncTask");
            var invokeAsyncBody  = new List <StatementSyntax>();
            var awaitExpression  = AwaitExpression(
                InvocationExpression(
                    invokeAsyncParam.Member("ConfigureAwait"),
                    ArgumentList(
                        SingletonSeparatedList(Argument(LiteralExpression(SyntaxKind.FalseLiteralExpression))))));

            if (result != null)
            {
                invokeAsyncBody.Add(
                    ExpressionStatement(
                        AssignmentExpression(
                            SyntaxKind.SimpleAssignmentExpression,
                            ThisExpression().Member(result.FieldName),
                            awaitExpression)));
            }
            else
            {
                invokeAsyncBody.Add(ExpressionStatement(AwaitExpression(invokeAsyncParam)));
            }

            var invokeAsync = LocalFunctionStatement(libraryTypes.ValueTask.ToTypeSyntax(), "InvokeAsync")
                              .WithModifiers(TokenList(Token(SyntaxKind.AsyncKeyword)))
                              .WithParameterList(
                ParameterList(
                    SingletonSeparatedList(
                        Parameter(invokeAsyncParam.Identifier).WithType(method.Method.ReturnType.ToTypeSyntax()))))
                              .WithBody(Block(invokeAsyncBody));

            // C#: return InvokeAsync(resultTask);
            body.Add(
                ReturnStatement(
                    InvocationExpression(
                        IdentifierName("InvokeAsync"),
                        ArgumentList(SingletonSeparatedList(Argument(resultTask))))));
            body.Add(invokeAsync);

            return(MethodDeclaration(libraryTypes.ValueTask.ToTypeSyntax(), "Invoke")
                   .WithParameterList(ParameterList())
                   .WithBody(Block(body))
                   .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword))));
        }