Merge pull request #4453 from lambdageek/bug-49721
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Description / ServiceContractGenerator.cs
index 433bf39568187c3f047580092641269ed9429126..8590c4538c532deab96263dff10a91815823c294 100644 (file)
@@ -29,12 +29,15 @@ using System;
 using System.CodeDom;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Configuration;
+using System.Linq;
 using System.Reflection;
 using System.Runtime.Serialization;
 using System.ServiceModel;
 using System.ServiceModel.Channels;
 using System.ServiceModel.Configuration;
+using System.Threading;
 using System.Xml.Schema;
 using System.Xml.Serialization;
 
@@ -48,39 +51,83 @@ namespace System.ServiceModel.Description
        {
                CodeCompileUnit ccu;
                ConfigurationType config;
+               CodeIdentifiers identifiers = new CodeIdentifiers ();
                Collection<MetadataConversionError> errors
                        = new Collection<MetadataConversionError> ();
                Dictionary<string,string> nsmappings
                        = new Dictionary<string,string> ();
                Dictionary<ContractDescription,Type> referenced_types
                        = new Dictionary<ContractDescription,Type> ();
+               Dictionary<ContractDescription,ContractCacheEntry> generated_contracts
+                       = new Dictionary<ContractDescription,ContractCacheEntry> ();
                ServiceContractGenerationOptions options;
-               Dictionary<QName, QName> imported_names = null;
+               Dictionary<QName, QName> imported_names
+                       = new Dictionary<QName, QName> ();
                ServiceContractGenerationContext contract_context;
                List<OPair> operation_contexts = new List<OPair> ();
 
+               XsdDataContractImporter data_contract_importer;
+               XmlSerializerMessageContractImporterInternal xml_serialization_importer;
+
+               class ContractCacheEntry {
+                       public ContractDescription Contract {
+                               get;
+                               private set;
+                       }
+
+                       public string ConfigurationName {
+                               get;
+                               private set;
+                       }
+
+                       public CodeTypeDeclaration TypeDeclaration {
+                               get;
+                               private set;
+                       }
+
+                       public bool GeneratedContractType {
+                               get; set;
+                       }
+
+                       public CodeTypeReference GetReference ()
+                       {
+                               return reference;
+                       }
+
+                       public ContractCacheEntry (ContractDescription cd, string config,
+                                                  CodeTypeDeclaration tdecl)
+                       {
+                               Contract = cd;
+                               ConfigurationName = config;
+                               TypeDeclaration = tdecl;
+                               reference = new CodeTypeReference (tdecl.Name);
+                       }
+
+                       readonly CodeTypeReference reference;
+               }
+
                public ServiceContractGenerator ()
                        : this (null, null)
                {
                }
 
-               public ServiceContractGenerator (CodeCompileUnit ccu)
-                       : this (ccu, null)
+               public ServiceContractGenerator (CodeCompileUnit targetCompileUnit)
+                       : this (targetCompileUnit, null)
                {
                }
 
-               public ServiceContractGenerator (ConfigurationType config)
-                       : this (null, config)
+               public ServiceContractGenerator (ConfigurationType targetConfig)
+                       : this (null, targetConfig)
                {
                }
 
-               public ServiceContractGenerator (CodeCompileUnit ccu, ConfigurationType config)
+               public ServiceContractGenerator (CodeCompileUnit targetCompileUnit, ConfigurationType targetConfig)
                {
-                       if (ccu == null)
+                       if (targetCompileUnit == null)
                                this.ccu = new CodeCompileUnit ();
                        else
-                               this.ccu = ccu;
-                       this.config = config;
+                               this.ccu = targetCompileUnit;
+                       this.config = targetConfig;
                        Options |= ServiceContractGenerationOptions.ChannelInterface | 
                                ServiceContractGenerationOptions.ClientClass;
                }
@@ -103,7 +150,11 @@ namespace System.ServiceModel.Description
                }
 
                bool GenerateAsync {
-                       get { return (options & ServiceContractGenerationOptions.AsynchronousMethods) != 0; }
+                       get { return GenerateEventBasedAsync || (options & ServiceContractGenerationOptions.AsynchronousMethods) != 0; }
+               }
+
+               bool GenerateEventBasedAsync {
+                       get { return (options & ServiceContractGenerationOptions.EventBasedAsynchronousMethods) != 0; }
                }
 
                public Dictionary<ContractDescription,Type> ReferencedTypes {
@@ -114,12 +165,26 @@ namespace System.ServiceModel.Description
                        get { return ccu; }
                }
 
-               [MonoTODO]
                public void GenerateBinding (Binding binding,
                        out string bindingSectionName,
                        out string configurationName)
                {
-                       throw new NotImplementedException ();
+                       if (config == null)
+                               throw new InvalidOperationException ();
+
+                       var element = ConfigUtil.FindCollectionElement (binding, config);
+                       if (element == null)
+                               throw new InvalidOperationException ();
+
+                       bindingSectionName = element.BindingName;
+
+                       int idx = 0;
+                       configurationName = binding.Name;
+                       while (element.ContainsKey (configurationName))
+                               configurationName = binding.Name + (++idx);
+
+                       if (!element.TryAdd (configurationName, binding, config))
+                               throw new InvalidOperationException ();
                }
 
                #region Service Contract Type
@@ -132,8 +197,9 @@ namespace System.ServiceModel.Description
                        ContractDescription contractDescription)
                {
                        CodeNamespace cns = GetNamespace (contractDescription.Namespace);
-                       imported_names = new Dictionary<QName, QName> ();
-                       var ret = ExportInterface (contractDescription, cns);
+                       var cache = ExportInterface_internal (contractDescription, cns);
+                       if (cache.GeneratedContractType)
+                               return cache.GetReference ();
 
                        // FIXME: handle duplex callback
 
@@ -143,6 +209,11 @@ namespace System.ServiceModel.Description
                        if ((Options & ServiceContractGenerationOptions.ClientClass) != 0)
                                GenerateProxyClass (contractDescription, cns);
 
+                       if (data_contract_importer != null)
+                               MergeCompileUnit (data_contract_importer.CodeCompileUnit, ccu);
+                       if (xml_serialization_importer != null)
+                               MergeCompileUnit (xml_serialization_importer.CodeCompileUnit, ccu);
+
                        // Process extensions. Class first, then methods.
                        // (built-in ones must present before processing class extensions).
                        foreach (var cb in contractDescription.Behaviors) {
@@ -153,18 +224,26 @@ namespace System.ServiceModel.Description
                        foreach (var opair in operation_contexts)
                                opair.Key.GenerateOperation (opair.Value);
 
-                       return ret;
+                       cache.GeneratedContractType = true;
+                       return cache.GetReference ();
                }
 
-               CodeNamespace GetNamespace (string ns)
+               CodeNamespace GetNamespace (string contractNs)
                {
-                       if (ns == null)
-                               ns = String.Empty;
+                       if (contractNs == null)
+                               contractNs = String.Empty;
+                       string csharpNs;
+                       if (nsmappings.ContainsKey (contractNs))
+                               csharpNs = nsmappings [contractNs];
+                       else if (nsmappings.ContainsKey ("*"))
+                               csharpNs = nsmappings ["*"];
+                       else
+                               csharpNs = string.Empty;
                        foreach (CodeNamespace cns in ccu.Namespaces)
-                               if (cns.Name == ns)
+                               if (cns.Name == csharpNs)
                                        return cns;
                        CodeNamespace ncns = new CodeNamespace ();
-                       //ncns.Name = ns;
+                       ncns.Name = csharpNs;
                        ccu.Namespaces.Add (ncns);
                        return ncns;
                }
@@ -182,6 +261,7 @@ namespace System.ServiceModel.Description
                        string name = cd.Name + "Client";
                        if (name [0] == 'I')
                                name = name.Substring (1);
+                       name = identifiers.AddUnique (name, null);
                        CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
                        if (type != null)
                                return; // already imported
@@ -255,11 +335,16 @@ namespace System.ServiceModel.Description
 
                        // service contract methods
                        AddImplementationClientMethods (type, cd);
+
+                       if (GenerateEventBasedAsync)
+                               foreach (var od in cd.Operations)
+                                       GenerateEventBasedAsyncSupport (type, od, cns);
                }
 
                void GenerateChannelInterface (ContractDescription cd, CodeNamespace cns)
                {
                        string name = cd.Name + "Channel";
+                       name = identifiers.AddUnique (name, null);
                        CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
                        if (type != null)
                                return;
@@ -275,25 +360,36 @@ namespace System.ServiceModel.Description
 
                CodeTypeReference ExportInterface (ContractDescription cd, CodeNamespace cns)
                {
-                       CodeTypeDeclaration type = GetTypeDeclaration (cns, cd.Name);
-                       if (type != null)
-                               return new CodeTypeReference (type.Name);
-                       type = new CodeTypeDeclaration ();
+                       var cache = ExportInterface_internal (cd, cns);
+                       return cache.GetReference ();
+               }
+
+               ContractCacheEntry ExportInterface_internal (ContractDescription cd, CodeNamespace cns)
+               {
+                       if (generated_contracts.ContainsKey (cd))
+                               return generated_contracts [cd];
+
+                       var type = new CodeTypeDeclaration ();
                        type.TypeAttributes = TypeAttributes.Interface;
                        type.TypeAttributes |= TypeAttributes.Public;
                        cns.Types.Add (type);
-                       type.Name = cd.Name;
+                       type.Name = identifiers.AddUnique (cd.Name, null);
+
+                       var configName = type.Name;
                        CodeAttributeDeclaration ad = 
                                new CodeAttributeDeclaration (
                                        new CodeTypeReference (
-                                               typeof (ServiceContractAttribute)));
+                                       typeof (ServiceContractAttribute)));
                        ad.Arguments.Add (new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (cd.Namespace)));
+                       ad.Arguments.Add (new CodeAttributeArgument ("ConfigurationName", new CodePrimitiveExpression (configName)));
                        type.CustomAttributes.Add (ad);
                        contract_context = new ServiceContractGenerationContext (this, cd, type);
-
+                       
                        AddOperationMethods (type, cd);
 
-                       return new CodeTypeReference (type.Name);
+                       var cache = new ContractCacheEntry (cd, configName, type);
+                       generated_contracts.Add (cd, cache);
+                       return cache;
                }
 
                void AddBeginAsyncArgs (CodeMemberMethod cm)
@@ -309,53 +405,15 @@ namespace System.ServiceModel.Description
                        foreach (OperationDescription od in cd.Operations) {
                                CodeMemberMethod syncMethod = null, beginMethod = null, endMethod = null;
 
-                               CodeMemberMethod cm = new CodeMemberMethod ();
-                               type.Members.Add (cm);
-                               if (GenerateAsync) {
-                                       cm.Name = "Begin" + od.Name;
-                                       beginMethod = cm;
-                               } else {
-                                       cm.Name = od.Name;
-                                       syncMethod = cm;
-                               }
                                CodeTypeReference returnTypeFromMessageContract = null;
+                               syncMethod = GenerateOperationMethod (type, cd, od, false, out returnTypeFromMessageContract);
+                               type.Members.Add (syncMethod);
 
-                               if (od.SyncMethod != null) {
-                                       ExportParameters (cm, od.SyncMethod.GetParameters ());
-                                       if (GenerateAsync) {
-                                               AddBeginAsyncArgs (cm);
-                                               cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
-                                       }
-                                       else
-                                               cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
-                               } else {
-                                       ExportMessages (od.Messages, cm, false);
-                                       returnTypeFromMessageContract = cm.ReturnType;
-                                       if (GenerateAsync) {
-                                               AddBeginAsyncArgs (cm);
-                                               cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
-                                       }
-                               }
-
-                               // [OperationContract (Action = "...", ReplyAction = "..")]
-                               CodeAttributeDeclaration ad =
-                                       new CodeAttributeDeclaration (
-                                               new CodeTypeReference (
-                                                       typeof (OperationContractAttribute)));
-                               foreach (MessageDescription md in od.Messages) {
-                                       if (md.Direction == MessageDirection.Input)
-                                               ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action)));
-                                       else
-                                               ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action)));
-                               }
-                               if (GenerateAsync)
-                                       ad.Arguments.Add (new CodeAttributeArgument ("AsyncPattern", new CodePrimitiveExpression (true)));
-                               cm.CustomAttributes.Add (ad);
-
-                               // For async mode, add EndXxx() too.
                                if (GenerateAsync) {
+                                       beginMethod = GenerateOperationMethod (type, cd, od, true, out returnTypeFromMessageContract);
+                                       type.Members.Add (beginMethod);
 
-                                       cm = new CodeMemberMethod ();
+                                       var cm = new CodeMemberMethod ();
                                        type.Members.Add (cm);
                                        cm.Name = "End" + od.Name;
                                        endMethod = cm;
@@ -377,6 +435,51 @@ namespace System.ServiceModel.Description
                        }
                }
 
+               CodeMemberMethod GenerateOperationMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnType)
+               {
+                       CodeMemberMethod cm = new CodeMemberMethod ();
+
+                       if (od.Behaviors.Find<XmlSerializerMappingBehavior> () != null)
+                               cm.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (XmlSerializerFormatAttribute))));
+
+                       if (async)
+                               cm.Name = "Begin" + od.Name;
+                       else
+                               cm.Name = od.Name;
+
+                       if (od.SyncMethod != null) {
+                               ExportParameters (cm, od.SyncMethod.GetParameters ());
+                               if (async) {
+                                       AddBeginAsyncArgs (cm);
+                                       cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
+                               }
+                               else
+                                       cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
+                               returnType = new CodeTypeReference (od.SyncMethod.ReturnType);
+                       } else {
+                               ExportMessages (od.Messages, cm, false);
+                               returnType = cm.ReturnType;
+                               if (async) {
+                                       AddBeginAsyncArgs (cm);
+                                       cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
+                               }
+                       }
+
+                       // [OperationContract (Action = "...", ReplyAction = "..")]
+                       var ad = new CodeAttributeDeclaration (new CodeTypeReference (typeof (OperationContractAttribute)));
+                       foreach (MessageDescription md in od.Messages) {
+                               if (md.Direction == MessageDirection.Input)
+                                       ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action)));
+                               else
+                                       ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action)));
+                       }
+                       if (async)
+                               ad.Arguments.Add (new CodeAttributeArgument ("AsyncPattern", new CodePrimitiveExpression (true)));
+                       cm.CustomAttributes.Add (ad);
+
+                       return cm;
+               }
+
                void ExportParameters (CodeMemberMethod method, ParameterInfo [] parameters)
                {
                        foreach (ParameterInfo pi in parameters)
@@ -389,52 +492,16 @@ namespace System.ServiceModel.Description
                void AddImplementationClientMethods (CodeTypeDeclaration type, ContractDescription cd)
                {
                        foreach (OperationDescription od in cd.Operations) {
-                               CodeMemberMethod cm = new CodeMemberMethod ();
-                               type.Members.Add (cm);
-                               if (GenerateAsync)
-                                       cm.Name = "Begin" + od.Name;
-                               else
-                                       cm.Name = od.Name;
-                               cm.Attributes = MemberAttributes.Public 
-                                               | MemberAttributes.Final;
+                               CodeMemberMethod cm;
                                CodeTypeReference returnTypeFromMessageContract = null;
+                               cm = GenerateImplementationClientMethod (type, cd, od, false, out returnTypeFromMessageContract);
+                               type.Members.Add (cm);
 
-                               List<CodeExpression> args = new List<CodeExpression> ();
-                               if (od.SyncMethod != null) {
-                                       ParameterInfo [] pars = od.SyncMethod.GetParameters ();
-                                       ExportParameters (cm, pars);
-                                       cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
-                                       int i = 0;
-                                       foreach (ParameterInfo pi in pars)
-                                               args.Add (new CodeArgumentReferenceExpression (pi.Name));
-                               } else {
-                                       args.AddRange (ExportMessages (od.Messages, cm, true));
-                                       returnTypeFromMessageContract = cm.ReturnType;
-                                       if (GenerateAsync) {
-                                               AddBeginAsyncArgs (cm);
-                                               cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
-                                       }
-                               }
-                               if (GenerateAsync) {
-                                       args.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
-                                       args.Add (new CodeArgumentReferenceExpression ("userState"));
-                               }
-
-                               CodeExpression call = new CodeMethodInvokeExpression (
-                                       new CodePropertyReferenceExpression (
-                                               new CodeBaseReferenceExpression (),
-                                               "Channel"),
-                                       cm.Name,
-                                       args.ToArray ());
-
-                               if (cm.ReturnType.BaseType == "System.Void")
-                                       cm.Statements.Add (new CodeExpressionStatement (call));
-                               else
-                                       cm.Statements.Add (new CodeMethodReturnStatement (call));
-
-                               // For async mode, add EndXxx() too.
                                if (!GenerateAsync)
-                                       return;
+                                       continue;
+
+                               cm = GenerateImplementationClientMethod (type, cd, od, true, out returnTypeFromMessageContract);
+                               type.Members.Add (cm);
 
                                // EndXxx() implementation
 
@@ -456,7 +523,7 @@ namespace System.ServiceModel.Description
                                if (od.EndMethod != null)
                                        resultArgName = od.EndMethod.GetParameters () [0].Name;
 
-                               call = new CodeMethodInvokeExpression (
+                               var call = new CodeMethodInvokeExpression (
                                        new CodePropertyReferenceExpression (
                                                new CodeBaseReferenceExpression (),
                                                "Channel"),
@@ -470,14 +537,278 @@ namespace System.ServiceModel.Description
                        }
                }
 
+               CodeMemberMethod GenerateImplementationClientMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnTypeFromMessageContract)
+               {
+                       CodeMemberMethod cm = new CodeMemberMethod ();
+                       if (async)
+                               cm.Name = "Begin" + od.Name;
+                       else
+                               cm.Name = od.Name;
+                       cm.Attributes = MemberAttributes.Public | MemberAttributes.Final;
+                       returnTypeFromMessageContract = null;
+
+                       List<CodeExpression> args = new List<CodeExpression> ();
+                       if (od.SyncMethod != null) {
+                               ParameterInfo [] pars = od.SyncMethod.GetParameters ();
+                               ExportParameters (cm, pars);
+                               cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
+                               int i = 0;
+                               foreach (ParameterInfo pi in pars)
+                                       args.Add (new CodeArgumentReferenceExpression (pi.Name));
+                       } else {
+                               args.AddRange (ExportMessages (od.Messages, cm, true));
+                               returnTypeFromMessageContract = cm.ReturnType;
+                               if (async) {
+                                       AddBeginAsyncArgs (cm);
+                                       cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
+                               }
+                       }
+                       if (async) {
+                               args.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
+                               args.Add (new CodeArgumentReferenceExpression ("userState"));
+                       }
+
+                       CodeExpression call = new CodeMethodInvokeExpression (
+                               new CodePropertyReferenceExpression (
+                                       new CodeBaseReferenceExpression (),
+                                       "Channel"),
+                               cm.Name,
+                               args.ToArray ());
+
+                       if (cm.ReturnType.BaseType == "System.Void")
+                               cm.Statements.Add (new CodeExpressionStatement (call));
+                       else
+                               cm.Statements.Add (new CodeMethodReturnStatement (call));
+                       return cm;
+               }
+
+               CodeMemberMethod FindByName (CodeTypeDeclaration type, string name)
+               {
+                       foreach (var m in type.Members) {
+                               var method = m as CodeMemberMethod;
+                               if (method != null && method.Name == name)
+                                       return method;
+                       }
+                       return null;
+               }
+
+               void GenerateEventBasedAsyncSupport (CodeTypeDeclaration type, OperationDescription od, CodeNamespace cns)
+               {
+                       var method = FindByName (type, od.Name) ?? FindByName (type, "Begin" + od.Name);
+                       var endMethod = method.Name == od.Name ? null : FindByName (type, "End" + od.Name);
+                       bool methodAsync = method.Name.StartsWith ("Begin", StringComparison.Ordinal);
+                       var resultType = endMethod != null ? endMethod.ReturnType : method.ReturnType;
+
+                       var thisExpr = new CodeThisReferenceExpression ();
+                       var baseExpr = new CodeBaseReferenceExpression ();
+                       var nullExpr = new CodePrimitiveExpression (null);
+                       var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
+
+                       // OnBeginXxx() implementation
+                       var cm = new CodeMemberMethod () {
+                               Name = "OnBegin" + od.Name,
+                               Attributes = MemberAttributes.Private | MemberAttributes.Final,
+                               ReturnType = asyncResultType
+                               };
+                       type.Members.Add (cm);
+
+                       AddMethodParam (cm, typeof (object []), "args");
+                       AddMethodParam (cm, typeof (AsyncCallback), "asyncCallback");
+                       AddMethodParam (cm, typeof (object), "userState");
+
+                       var call = new CodeMethodInvokeExpression (
+                               thisExpr,
+                               "Begin" + od.Name);
+                       for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) {
+                               var p = method.Parameters [idx];
+                               cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name, new CodeCastExpression (p.Type, new CodeArrayIndexerExpression (new CodeArgumentReferenceExpression ("args"), new CodePrimitiveExpression (idx)))));
+                               call.Parameters.Add (new CodeVariableReferenceExpression (p.Name));
+                       }
+                       call.Parameters.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
+                       call.Parameters.Add (new CodeArgumentReferenceExpression ("userState"));
+                       cm.Statements.Add (new CodeMethodReturnStatement (call));
+
+                       // OnEndXxx() implementation
+                       cm = new CodeMemberMethod () {
+                               Name = "OnEnd" + od.Name,
+                               Attributes = MemberAttributes.Private | MemberAttributes.Final,
+                               ReturnType = new CodeTypeReference (typeof (object [])) };
+                       type.Members.Add (cm);
+
+                       AddMethodParam (cm, typeof (IAsyncResult), "result");
+
+                       var outArgRefs = new List<CodeVariableReferenceExpression> ();
+
+                       for (int idx = 0; idx < method.Parameters.Count; idx++) {
+                               var p = method.Parameters [idx];
+                               if (p.Direction != FieldDirection.In) {
+                                       cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name));
+                                       outArgRefs.Add (new CodeVariableReferenceExpression (p.Name)); // FIXME: should this work? They need "out" or "ref" modifiers.
+                               }
+                       }
+
+                       call = new CodeMethodInvokeExpression (
+                               thisExpr,
+                               "End" + od.Name,
+                               new CodeArgumentReferenceExpression ("result"));
+                       call.Parameters.AddRange (outArgRefs.Cast<CodeExpression> ().ToArray ()); // questionable
+
+                       var retCreate = new CodeArrayCreateExpression (typeof (object));
+                       if (resultType.BaseType == "System.Void")
+                               cm.Statements.Add (call);
+                       else {
+                               cm.Statements.Add (new CodeVariableDeclarationStatement (typeof (object), "__ret", call));
+                               retCreate.Initializers.Add (new CodeVariableReferenceExpression ("__ret"));
+                       }
+                       foreach (var outArgRef in outArgRefs)
+                               retCreate.Initializers.Add (new CodeVariableReferenceExpression (outArgRef.VariableName));
+
+                       cm.Statements.Add (new CodeMethodReturnStatement (retCreate));
+
+                       // OnXxxCompleted() implementation
+                       cm = new CodeMemberMethod () {
+                               Name = "On" + od.Name + "Completed",
+                               Attributes = MemberAttributes.Private | MemberAttributes.Final };
+                       type.Members.Add (cm);
+
+                       AddMethodParam (cm, typeof (object), "state");
+
+                       string argsname = identifiers.AddUnique (od.Name + "CompletedEventArgs", null);
+                       var iaargs = new CodeTypeReference ("InvokeAsyncCompletedEventArgs"); // avoid messy System.Type instance for generic nested type :|
+                       var iaref = new CodeVariableReferenceExpression ("args");
+                       var methodEventArgs = new CodeObjectCreateExpression (new CodeTypeReference (argsname),
+                               new CodePropertyReferenceExpression (iaref, "Results"),
+                               new CodePropertyReferenceExpression (iaref, "Error"),
+                               new CodePropertyReferenceExpression (iaref, "Cancelled"),
+                               new CodePropertyReferenceExpression (iaref, "UserState"));
+                       cm.Statements.Add (new CodeConditionStatement (
+                               new CodeBinaryOperatorExpression (
+                                       new CodeEventReferenceExpression (thisExpr, od.Name + "Completed"), CodeBinaryOperatorType.IdentityInequality, nullExpr),
+                               new CodeVariableDeclarationStatement (iaargs, "args", new CodeCastExpression (iaargs, new CodeArgumentReferenceExpression ("state"))),
+                               new CodeExpressionStatement (new CodeMethodInvokeExpression (thisExpr, od.Name + "Completed", thisExpr, methodEventArgs))));
+
+                       // delegate fields
+                       type.Members.Add (new CodeMemberField (new CodeTypeReference ("BeginOperationDelegate"), "onBegin" + od.Name + "Delegate"));
+                       type.Members.Add (new CodeMemberField (new CodeTypeReference ("EndOperationDelegate"), "onEnd" + od.Name + "Delegate"));
+                       type.Members.Add (new CodeMemberField (new CodeTypeReference (typeof (SendOrPostCallback)), "on" + od.Name + "CompletedDelegate"));
+
+                       // XxxCompletedEventArgs class
+                       var argsType = new CodeTypeDeclaration (argsname);
+                       argsType.BaseTypes.Add (new CodeTypeReference (typeof (AsyncCompletedEventArgs)));
+                       cns.Types.Add (argsType);
+
+                       var argsCtor = new CodeConstructor () {
+                               Attributes = MemberAttributes.Public | MemberAttributes.Final };
+                       argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object []), "results"));
+                       argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (Exception), "error"));
+                       argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (bool), "cancelled"));
+                       argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
+                       argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("error"));
+                       argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("cancelled"));
+                       argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("userState"));
+                       var resultsField = new CodeFieldReferenceExpression (thisExpr, "results");
+                       argsCtor.Statements.Add (new CodeAssignStatement (resultsField, new CodeArgumentReferenceExpression ("results")));
+                       argsType.Members.Add (argsCtor);
+
+                       argsType.Members.Add (new CodeMemberField (typeof (object []), "results"));
+
+                       if (resultType.BaseType != "System.Void") {
+                               var resultProp = new CodeMemberProperty {
+                                       Name = "Result",
+                                       Type = resultType,
+                                       Attributes = MemberAttributes.Public | MemberAttributes.Final };
+                               resultProp.GetStatements.Add (new CodeMethodReturnStatement (new CodeCastExpression (resultProp.Type, new CodeArrayIndexerExpression (resultsField, new CodePrimitiveExpression (0)))));
+                               argsType.Members.Add (resultProp);
+                       }
+
+                       // event field
+                       var handlerType = new CodeTypeReference (typeof (EventHandler<>));
+                       handlerType.TypeArguments.Add (new CodeTypeReference (argsType.Name));
+                       type.Members.Add (new CodeMemberEvent () {
+                               Name = od.Name + "Completed",
+                               Type = handlerType,
+                               Attributes = MemberAttributes.Public | MemberAttributes.Final });
+
+                       // XxxAsync() implementations
+                       bool hasAsync = false;
+                       foreach (int __x in Enumerable.Range (0, 2)) {
+                               cm = new CodeMemberMethod ();
+                               type.Members.Add (cm);
+                               cm.Name = od.Name + "Async";
+                               cm.Attributes = MemberAttributes.Public 
+                                               | MemberAttributes.Final;
+
+                               var inArgs = new List<CodeParameterDeclarationExpression > ();
+
+                               for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) {
+                                       var pd = method.Parameters [idx];
+                                       inArgs.Add (pd);
+                                       cm.Parameters.Add (pd);
+                               }
+
+                               // First one is overload without asyncState arg.
+                               if (!hasAsync) {
+                                       call = new CodeMethodInvokeExpression (thisExpr, cm.Name, inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ());
+                                       call.Parameters.Add (nullExpr);
+                                       cm.Statements.Add (new CodeExpressionStatement (call));
+                                       hasAsync = true;
+                                       continue;
+                               }
+
+                               // Second one is the primary one.
+
+                               cm.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
+
+                               // if (onBeginBarOperDelegate == null) onBeginBarOperDelegate = new BeginOperationDelegate (OnBeginBarOper);
+                               // if (onEndBarOperDelegate == null) onEndBarOperDelegate = new EndOperationDelegate (OnEndBarOper);
+                               // if (onBarOperCompletedDelegate == null) onBarOperCompletedDelegate = new BeginOperationDelegate (OnBarOperCompleted);
+                               var beginOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onBegin" + od.Name + "Delegate");
+                               var endOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onEnd" + od.Name + "Delegate");
+                               var operCompletedDelegateRef = new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate");
+
+                               var ifstmt = new CodeConditionStatement (
+                                       new CodeBinaryOperatorExpression (beginOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
+                                       new CodeAssignStatement (beginOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("BeginOperationDelegate"), thisExpr, "OnBegin" + od.Name)));
+                               cm.Statements.Add (ifstmt);
+                               ifstmt = new CodeConditionStatement (
+                                       new CodeBinaryOperatorExpression (endOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
+                                       new CodeAssignStatement (endOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("EndOperationDelegate"), thisExpr, "OnEnd" + od.Name)));
+                               cm.Statements.Add (ifstmt);
+                               ifstmt = new CodeConditionStatement (
+                                       new CodeBinaryOperatorExpression (operCompletedDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
+                                       new CodeAssignStatement (operCompletedDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference (typeof (SendOrPostCallback)), thisExpr, "On" + od.Name + "Completed")));
+                               cm.Statements.Add (ifstmt);
+
+                               // InvokeAsync (onBeginBarOperDelegate, inValues, onEndBarOperDelegate, onBarOperCompletedDelegate, userState);
+
+                               inArgs.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
+
+                               var args = new List<CodeExpression> ();
+                               args.Add (beginOperDelegateRef);
+                               args.Add (new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
+                               args.Add (endOperDelegateRef);
+                               args.Add (new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate"));
+                               args.Add (new CodeArgumentReferenceExpression ("userState"));
+                               call = new CodeMethodInvokeExpression (baseExpr, "InvokeAsync", args.ToArray ());
+                               cm.Statements.Add (new CodeExpressionStatement (call));
+                       }
+               }
+
+               void AddMethodParam (CodeMemberMethod cm, Type type, string name)
+               {
+                       cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));
+               }
+
+               const string ms_arrays_ns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
+
                private CodeExpression[] ExportMessages (MessageDescriptionCollection messages, CodeMemberMethod method, bool return_args)
                {
                        CodeExpression [] args = null;
                        foreach (MessageDescription md in messages) {
                                if (md.Direction == MessageDirection.Output) {
                                        if (md.Body.ReturnValue != null) {
-                                               ExportDataContract (md.Body.ReturnValue.XmlTypeMapping);        
-                                               method.ReturnType = new CodeTypeReference (md.Body.ReturnValue.TypeName.Name);
+                                               ExportDataContract (md.Body.ReturnValue);
+                                               method.ReturnType = md.Body.ReturnValue.CodeTypeReference;
                                        }
                                        continue;
                                }
@@ -487,11 +818,11 @@ namespace System.ServiceModel.Description
 
                                MessagePartDescriptionCollection parts = md.Body.Parts;
                                for (int i = 0; i < parts.Count; i++) {
-                                       ExportDataContract (parts [i].XmlTypeMapping);  
+                                       ExportDataContract (parts [i]);
 
                                        method.Parameters.Add (
                                                new CodeParameterDeclarationExpression (
-                                                       new CodeTypeReference (parts [i].TypeName.Name),
+                                                       parts [i].CodeTypeReference,
                                                        parts [i].Name));
 
                                        if (return_args)
@@ -504,117 +835,76 @@ namespace System.ServiceModel.Description
 
                #endregion
 
-               [MonoTODO]
                public CodeTypeReference GenerateServiceEndpoint (
                        ServiceEndpoint endpoint,
                        out ChannelEndpointElement channelElement)
                {
-                       throw new NotImplementedException ();
-               }
-
-               private void ExportDataContract (XmlTypeMapping mapping)
-               {
-                       if (mapping == null)
-                               return;
-
-                       QName qname = new QName (mapping.TypeName, mapping.Namespace);
-                       if (imported_names.ContainsKey (qname))
-                               return;
+                       if (config == null)
+                               throw new InvalidOperationException ();
 
-                       CodeNamespace cns = new CodeNamespace ();
+                       var cd = endpoint.Contract;
+                       var cns = GetNamespace (cd.Namespace);
+                       var cache = ExportInterface_internal (cd, cns);
 
-                       XmlCodeExporter xce = new XmlCodeExporter (cns);
-                       xce.ExportTypeMapping (mapping);
+                       string bindingSectionName, configurationName;
+                       GenerateBinding (endpoint.Binding, out bindingSectionName, out configurationName);
 
-                       List <CodeTypeDeclaration> to_remove = new List <CodeTypeDeclaration> ();
-                       
-                       //Process the types just generated
-                       //FIXME: Iterate and assign the types to correct namespaces
-                       //At the end, add all those namespaces to the ccu
-                       foreach (CodeTypeDeclaration type in cns.Types) {
-                               string ns = GetXmlNamespace (type);
-                               if (ns == null)
-                                       //FIXME: do what here?
-                                       continue;
-
-                               QName type_name = new QName (type.Name, ns);
-                               if (imported_names.ContainsKey (type_name)) {
-                                       //Type got reemitted, so remove it!
-                                       to_remove.Add (type);
-                                       continue;
-                               }
+                       channelElement = new ChannelEndpointElement ();
+                       channelElement.Binding = bindingSectionName;
+                       channelElement.BindingConfiguration = configurationName;
+                       channelElement.Name = configurationName;
+                       channelElement.Contract = cache.ConfigurationName;
+                       channelElement.Address = endpoint.Address.Uri;
 
-                               imported_names [type_name] = type_name;
+                       var section = (ClientSection)config.GetSection ("system.serviceModel/client");
+                       section.Endpoints.Add (channelElement);
 
-                               type.Comments.Clear ();
-                               //Custom Attributes
-                               type.CustomAttributes.Clear ();
+                       return cache.GetReference ();
+               }
 
-                               if (type.IsEnum)
-                                       continue;
-       
-                               type.CustomAttributes.Add (
-                                       new CodeAttributeDeclaration (
-                                               new CodeTypeReference ("System.CodeDom.Compiler.GeneratedCodeAttribute"),
-                                               new CodeAttributeArgument (new CodePrimitiveExpression ("System.Runtime.Serialization")),
-                                               new CodeAttributeArgument (new CodePrimitiveExpression ("3.0.0.0"))));
-                       
-                               type.CustomAttributes.Add (
-                                       new CodeAttributeDeclaration (
-                                               new CodeTypeReference ("System.Runtime.Serialization.DataContractAttribute")));
-
-                               //BaseType and interface
-                               type.BaseTypes.Add (new CodeTypeReference (typeof (object)));
-                               type.BaseTypes.Add (new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject"));
-
-                               foreach (CodeTypeMember mbr in type.Members) {
-                                       CodeMemberProperty p = mbr as CodeMemberProperty;
-                                       if (p == null)
-                                               continue;
-
-                                       if ((p.Attributes & MemberAttributes.Public) == MemberAttributes.Public) {
-                                               //FIXME: Clear all attributes or only XmlElementAttribute?
-                                               p.CustomAttributes.Clear ();
-                                               p.CustomAttributes.Add (new CodeAttributeDeclaration (
-                                                       new CodeTypeReference ("System.Runtime.Serialization.DataMemberAttribute")));
-
-                                               p.Comments.Clear ();
+               void MergeCompileUnit (CodeCompileUnit from, CodeCompileUnit to)
+               {
+                       if (from == to)
+                               return;
+                       foreach (CodeNamespace fns in from.Namespaces) {
+                               bool merged = false;
+                               foreach (CodeNamespace tns in to.Namespaces)
+                                       if (fns.Name == tns.Name) {
+                                               // namespaces are merged.
+                                               MergeNamespace (fns, tns);
+                                               merged = true;
+                                               break;
                                        }
-                               }
-
-                               //Fields
-                               CodeMemberField field = new CodeMemberField (
-                                       new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject"),
-                                       "extensionDataField");
-                               field.Attributes = MemberAttributes.Private | MemberAttributes.Final;
-                               type.Members.Add (field);
-
-                               //Property 
-                               CodeMemberProperty prop = new CodeMemberProperty ();
-                               prop.Type = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
-                               prop.Name = "ExtensionData";
-                               prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
-
-                               //Get
-                               prop.GetStatements.Add (new CodeMethodReturnStatement (
-                                       new CodeFieldReferenceExpression (
-                                       new CodeThisReferenceExpression (),
-                                       "extensionDataField")));
-
-                               //Set
-                               prop.SetStatements.Add (new CodeAssignStatement (
-                                       new CodeFieldReferenceExpression (
-                                       new CodeThisReferenceExpression (),
-                                       "extensionDataField"),
-                                       new CodePropertySetValueReferenceExpression ()));
-
-                               type.Members.Add (prop);
+                               if (!merged)
+                                       to.Namespaces.Add (fns);
                        }
+               }
 
-                       foreach (CodeTypeDeclaration type in to_remove)
-                               cns.Types.Remove (type);
+               // existing type is skipped.
+               void MergeNamespace (CodeNamespace from, CodeNamespace to)
+               {
+                       foreach (CodeTypeDeclaration ftd in from.Types) {
+                               bool skip = false;
+                               foreach (CodeTypeDeclaration ttd in to.Types)
+                                       if (ftd.Name == ttd.Name) {
+                                               skip = true;
+                                               break;
+                                       }
+                               if (!skip)
+                                       to.Types.Add (ftd);
+                       }
+               }
 
-                       ccu.Namespaces.Add (cns);
+               private void ExportDataContract (MessagePartDescription md)
+               {
+                       if (data_contract_importer == null)
+                               data_contract_importer = md.DataContractImporter;
+                       else if (md.DataContractImporter != null && data_contract_importer != md.DataContractImporter)
+                               throw new Exception ("INTERNAL ERROR: should not happen");
+                       if (xml_serialization_importer == null)
+                               xml_serialization_importer = md.XmlSerializationImporter;
+                       else if (md.XmlSerializationImporter != null && xml_serialization_importer != md.XmlSerializationImporter)
+                               throw new Exception ("INTERNAL ERROR: should not happen");
                }
                
                private string GetXmlNamespace (CodeTypeDeclaration type)