File: CodeDomFixup\CodeDomVisitors\MakeOldAsyncMethodsPrivate.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using Microsoft.CodeDom;
using System.Collections.Generic;
 
namespace Microsoft.Tools.ServiceModel.Svcutil
{
    internal class MakeOldAsyncMethodsPrivate : ClientClassVisitor
    {
        private Dictionary<string, PrivateInterfaceMethod> _privateIfaceMethods;
 
        private class PrivateInterfaceMethod
        {
            internal PrivateInterfaceMethod(CodeTypeReference ifaceType)
            {
                this.InterfaceType = ifaceType;
            }
            internal readonly CodeTypeReference InterfaceType;
        }
 
        protected override void VisitClientClass(CodeTypeDeclaration type)
        {
            base.VisitClientClass(type);
 
            _privateIfaceMethods = new Dictionary<string, PrivateInterfaceMethod>();
            CollectionHelpers.MapList<CodeMemberMethod>(type.Members, MapMethodFirstPass, null);
        }
 
        // first-pass looks at each public BeginFoo or EndFoo method and makes it private.
        // for methods that are interface implementations, we need to remember them so that we can update
        // the calling code in the second pass
        private bool MapMethodFirstPass(CodeMemberMethod method)
        {
            if (method != null && (CodeDomHelpers.IsBeginMethod(method) || CodeDomHelpers.IsEndMethod(method)) && IsPublic(method.Attributes))
            {
                if (method.ImplementationTypes.Count == 0)
                {
                    // doesn't impl an iface method, just make it private, and remember it for the second pass
                    method.Attributes = MakePrivate(method.Attributes);
 
                    // clobber existing entries -- non iface-methods take precedence
                    _privateIfaceMethods[method.Name] = new PrivateInterfaceMethod(null);
                }
                else
                {
                    // impls an iface method, make it a private impl, and remember it for the second pass
                    CodeTypeReference ifaceType = method.ImplementationTypes[0];
                    method.ImplementationTypes.Clear();
                    method.PrivateImplementationType = ifaceType;
 
                    if (!_privateIfaceMethods.ContainsKey(method.Name))
                    {
                        // only add it if it wasn't already there -- non-iface methods take precedence
                        _privateIfaceMethods.Add(method.Name, new PrivateInterfaceMethod(ifaceType));
                    }
                }
            }
            return true; // don't remove
        }
 
        protected override void OnExitSpecificType()
        {
            base.OnExitSpecificType();
 
            _privateIfaceMethods = null;
        }
 
        protected override void Visit(CodeMethodInvokeExpression methodInvoke)
        {
            base.Visit(methodInvoke);
 
            if (_privateIfaceMethods != null)
            {
                // we got a method call, let's see if it's one of the private impls we need to update
                // few criteria:
                //   a) TargetObject will be a CodeThisReferenceExpression
                //   b) MethodName will be in our table
                //   c) PrivateInterfaceMethod.InterfaceType will be set
                PrivateInterfaceMethod targetMethod = null;
                if (methodInvoke.Method.TargetObject is CodeThisReferenceExpression &&
                    _privateIfaceMethods.TryGetValue(methodInvoke.Method.MethodName, out targetMethod))
                {
                    if (targetMethod.InterfaceType != null)
                    {
                        methodInvoke.Method.TargetObject = new CodeCastExpression(targetMethod.InterfaceType, methodInvoke.Method.TargetObject);
                    }
                }
            }
        }
 
        private static bool IsPublic(MemberAttributes attrs)
        {
            return (attrs & MemberAttributes.Public) == MemberAttributes.Public;
        }
 
        private static MemberAttributes MakePrivate(MemberAttributes attrs)
        {
            return (attrs & ~MemberAttributes.AccessMask) | MemberAttributes.Private;
        }
    }
}