示例#1
0
        public Task <ClassDeclarationSyntax> GenerateClientClass(SemanticModel semanticModel, SyntaxGenerator gen, INamedTypeSymbol proxyInterface, string name, Accessibility accessibility, bool includeCancellableAsyncMethods, bool suppressWarningComments, MemberAccessibility constructorAccessibility, bool withInternalProxy)
        {
            if (name == null)
            {
                if (proxyInterface.Name.StartsWith("I"))
                {
                    name = proxyInterface.Name.Substring(1);
                }

                if (name.EndsWith("Proxy"))
                {
                    name = name.Substring(0, name.Length - "Proxy".Length);
                }

                if (!name.EndsWith("Client"))
                {
                    name = name + "Client";
                }
            }


            SyntaxNode targetClass = gen.ClassDeclaration(name,
                                                          baseType: gen.TypeExpression(semanticModel.Compilation.RequireType <MarshalByRefObject>()),
                                                          accessibility: accessibility,
                                                          modifiers: DeclarationModifiers.Sealed);

            targetClass = gen.AddWarningCommentIf(!suppressWarningComments, targetClass);

            targetClass = gen.AddInterfaceType(targetClass, gen.TypeExpression(semanticModel.Compilation.GetSpecialType(SpecialType.System_IDisposable)));
            targetClass = gen.AddInterfaceType(targetClass, gen.TypeExpression(proxyInterface));

            IEnumerable <IMethodSymbol> methods = GetOperationContractMethods(semanticModel.Compilation, proxyInterface).ToArray();

            GenerationNameTable nameTable = new GenerationNameTable(methods.Select(m => m.Name).Concat(new[] { name }));


            #region Private Fields

            // ==> private IProxy m_cachedProxy;
            SyntaxNode cachedProxyField =
                gen.FieldDeclaration(nameTable[MemberNames.CachedProxyField], gen.TypeExpression(proxyInterface), Accessibility.Private, DeclarationModifiers.None)
                .PrependLeadingTrivia(gen.CreateRegionTrivia("Private Fields"));

            targetClass = gen.AddMembers(targetClass, cachedProxyField);

            // ==> private readonly Func<IProxy> m_proxyFactory;
            SyntaxNode proxyFactoryTypeExpression = gen.TypeExpression(semanticModel.Compilation.RequireTypeByMetadataName("System.Func`1").Construct(proxyInterface));

            targetClass = gen.AddMembers(targetClass, gen.FieldDeclaration(nameTable[MemberNames.ProxyFactoryField], proxyFactoryTypeExpression, Accessibility.Private, DeclarationModifiers.ReadOnly)
                                         .AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia());

            #endregion


            #region Constructors

            // Constructor
            SyntaxNode constructor = gen.ConstructorDeclaration(
                parameters: new[] { gen.ParameterDeclaration("proxyFactory", proxyFactoryTypeExpression) },
                accessibility: withInternalProxy?Accessibility.Private: ToAccessibility(constructorAccessibility)
                );

            constructor = gen.AddWarningCommentIf(!suppressWarningComments, constructor);
            constructor = constructor.PrependLeadingTrivia(gen.CreateRegionTrivia("Constructors"));

            constructor = gen.WithStatements(constructor,
                                             new[]
            {
                // ==> if (proxyFactory == null)
                // ==>   throw new System.ArgumentNullException("proxyFactory");
                gen.ThrowIfNullStatement("proxyFactory"),

                // ==> m_proxyFactory = proxyFactory
                gen.AssignmentStatement(
                    gen.MemberAccessExpression(
                        gen.ThisExpression(),
                        gen.IdentifierName(nameTable[MemberNames.ProxyFactoryField])),
                    gen.IdentifierName("proxyFactory")
                    )
            }
                                             ).AddNewLineTrivia();

            if (!withInternalProxy)
            {
                constructor = constructor.AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia();
            }

            targetClass = gen.AddMembers(targetClass, constructor);

            ClassDeclarationSyntax proxyClass = null;
            if (withInternalProxy)
            {
                IEnumerable <IMethodSymbol> ctors;
                proxyClass = GenerateProxyClass(semanticModel, gen, proxyInterface, nameTable[MemberNames.ProxyClass], Accessibility.Private, suppressWarningComments, MemberAccessibility.Public, out ctors)
                             .PrependLeadingTrivia(gen.CreateRegionTrivia("Proxy Class").Insert(0, gen.NewLine()))
                             .AddTrailingTrivia(gen.CreateEndRegionTrivia());

                // Generate one constructor for each of the proxy's constructors.
                foreach (var ctorEntry in ctors.AsSmartEnumerable())
                {
                    var ctor       = ctorEntry.Value;
                    var targetCtor = gen.ConstructorDeclaration(ctor);

                    var lambda = gen.ValueReturningLambdaExpression(
                        gen.ObjectCreationExpression(gen.IdentifierName(gen.GetName(proxyClass)), ctor.Parameters.Select(p => gen.IdentifierName(p.Name)))
                        );

                    targetCtor = gen.WithThisConstructorInitializer(targetCtor, new[] { lambda });

                    targetCtor = gen.AddWarningCommentIf(!suppressWarningComments, targetCtor);
                    targetCtor = gen.WithAccessibility(targetCtor, ToAccessibility(constructorAccessibility));

                    if (ctorEntry.IsLast)
                    {
                        targetCtor = targetCtor.AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia();
                    }

                    targetClass = gen.AddMembers(targetClass, targetCtor.AddNewLineTrivia());
                }
            }

            #endregion

            #region Operation Contract Methods

            // ==> catch
            // ==> {
            // ==>    this.CloseProxy(false);
            // ==>    throw;
            // ==> }
            var catchAndCloseProxyStatement = gen.CatchClause(new SyntaxNode[]
            {
                // ==> this.CloseProxy(false);
                gen.ExpressionStatement(
                    gen.InvocationExpression(
                        gen.MemberAccessExpression(
                            gen.ThisExpression(),
                            nameTable[MemberNames.CloseProxyMethod]
                            ),
                        gen.FalseLiteralExpression()
                        )
                    ),

                // throw;
                gen.ThrowStatement()
            });


            foreach (var sourceMethodEntry in methods.AsSmartEnumerable())
            {
                var sourceMethod = sourceMethodEntry.Value;

                using (nameTable.PushScope(sourceMethod.Parameters.Select(p => p.Name)))
                {
                    bool isAsync = ReturnsTask(semanticModel.Compilation, sourceMethod);
                    bool isVoid  = sourceMethod.ReturnType.SpecialType == SpecialType.System_Void || sourceMethod.ReturnType.Equals(semanticModel.Compilation.RequireType <Task>());

                    SyntaxNode targetMethod = gen.MethodDeclaration(sourceMethod);

                    if (sourceMethodEntry.IsFirst)
                    {
                        targetMethod = targetMethod.PrependLeadingTrivia(gen.CreateRegionTrivia("Contract Methods")).AddLeadingTrivia(gen.NewLine());
                    }

                    targetMethod = gen.AddWarningCommentIf(!suppressWarningComments, targetMethod);

                    targetMethod = gen.WithModifiers(targetMethod, isAsync ? DeclarationModifiers.Async : DeclarationModifiers.None);


                    targetMethod = gen.WithStatements(targetMethod, new SyntaxNode[]
                    {
                        // ==> try {
                        gen.TryCatchStatement(new SyntaxNode[]
                        {
                            CreateProxyVaraibleDeclaration(gen, nameTable, isAsync),
                            CreateProxyInvocationStatement(semanticModel.Compilation, gen, nameTable, sourceMethod)
                        }, new SyntaxNode[]
                        {
                            catchAndCloseProxyStatement
                        }
                                              )
                    });

                    targetMethod = targetMethod.AddNewLineTrivia();

                    if (sourceMethodEntry.IsLast && !(isAsync && includeCancellableAsyncMethods))
                    {
                        targetMethod = targetMethod.AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia();
                    }

                    targetClass = gen.AddMembers(targetClass, targetMethod);

                    if (isAsync && includeCancellableAsyncMethods)
                    {
                        targetMethod = gen.MethodDeclaration(sourceMethod);
                        targetMethod = gen.AddParameters(targetMethod, new[] { gen.ParameterDeclaration(nameTable[MemberNames.CancellationTokenParameter], gen.TypeExpression(semanticModel.Compilation.RequireType <CancellationToken>())) });
                        targetMethod = gen.WithModifiers(targetMethod, isAsync ? DeclarationModifiers.Async : DeclarationModifiers.None);


                        targetMethod = gen.WithStatements(targetMethod, new SyntaxNode[]
                        {
                            // ==> try {
                            gen.TryCatchStatement(new SyntaxNode[]
                            {
                                CreateProxyVaraibleDeclaration(gen, nameTable, isAsync),
                                CreateCancellableProxyInvocationStatement(semanticModel.Compilation, gen, nameTable, sourceMethod)
                            }, new SyntaxNode[]
                            {
                                catchAndCloseProxyStatement
                            }
                                                  )
                        });


                        targetMethod = gen.AddWarningCommentIf(!suppressWarningComments, targetMethod.AddNewLineTrivia());

                        if (sourceMethodEntry.IsLast)
                        {
                            targetMethod = targetMethod.AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia();
                        }

                        targetClass = gen.AddMembers(targetClass, targetMethod);
                    }
                }
            }

            #endregion

            #region Internal Methods

            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateGetProxyMethod(semanticModel.Compilation, gen, proxyInterface, nameTable, false).AddLeadingTrivia(gen.CreateRegionTrivia("Private Methods")).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateGetProxyMethod(semanticModel.Compilation, gen, proxyInterface, nameTable, true).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateStaticCloseProxyMethod(semanticModel.Compilation, gen, nameTable, false).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateStaticCloseProxyMethod(semanticModel.Compilation, gen, nameTable, true).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateCloseProxyMethod(semanticModel.Compilation, gen, nameTable, false).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateCloseProxyMethod(semanticModel.Compilation, gen, nameTable, true).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateEnsureProxyMethod(semanticModel.Compilation, gen, nameTable, false).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, gen.AddWarningCommentIf(!suppressWarningComments, CreateEnsureProxyMethod(semanticModel.Compilation, gen, nameTable, true).AddTrailingTrivia(gen.CreateEndRegionTrivia()).AddNewLineTrivia()));
            targetClass = gen.AddMembers(targetClass, CreateDisposeMethods(semanticModel.Compilation, gen, nameTable, suppressWarningComments));

            if (withInternalProxy)
            {
                targetClass = gen.AddMembers(targetClass, proxyClass);
            }

            #endregion


            targetClass = AddGeneratedCodeAttribute(gen, targetClass);
            return(Task.FromResult((ClassDeclarationSyntax)targetClass));
        }