Merge pull request #5675 from mono/glib-debug-symbols
[mono.git] / mcs / tools / svcutil / MoonlightChannelBaseExtension.cs
1 //
2 // MoonlightChannelBaseExtension.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2009 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.CodeDom;
30 using System.CodeDom.Compiler;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.ServiceModel;
37 using System.ServiceModel.Channels;
38 using System.ServiceModel.Description;
39 using System.ServiceModel.Dispatcher;
40 using System.Threading;
41
42 namespace Mono.ServiceContractTool
43 {
44         class MoonlightChannelBaseContext
45         {
46                 public MoonlightChannelBaseContractExtension Contract;
47                 public List<MoonlightChannelBaseOperationExtension> Operations = new List<MoonlightChannelBaseOperationExtension> ();
48
49                 public CodeTypeDeclaration ClientType { get; set; }
50                 public CodeTypeDeclaration ChannelType { get; set; }
51
52                 public void FindClientType (ServiceContractGenerationContext context)
53                 {
54                         var cd = context.Contract;
55                         string name = cd.Name + "Client";
56                         if (name [0] == 'I')
57                                 name = name.Substring (1);
58
59                         foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces)
60                                 foreach (CodeTypeDeclaration ct in cns.Types)
61                                         if (ct == context.ContractType)
62                                                 foreach (CodeTypeDeclaration ct2 in cns.Types)
63                                                         if (ct2.Name == name) {
64                                                                 ClientType = ct2;
65                                                                 return;
66                                                         }
67                         throw new Exception (String.Format ("Contract '{0}' not found", name));
68                 }
69
70                 public void Fixup ()
71                 {
72                         Contract.Fixup ();
73                         foreach (var op in Operations)
74                                 op.Fixup ();
75                 }
76         }
77
78         class MoonlightChannelBaseContractExtension : IContractBehavior, IServiceContractGenerationExtension
79         {
80                 public MoonlightChannelBaseContractExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
81                 {
82                         ml_context = mlContext;
83                         generate_sync = generateSync;
84                 }
85
86                 MoonlightChannelBaseContext ml_context;
87                 bool generate_sync;
88
89                 // IContractBehavior
90                 public void AddBindingParameters (ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
91                 {
92                         throw new NotSupportedException ();
93                 }
94
95                 public void ApplyClientBehavior (
96                         ContractDescription description,
97                         ServiceEndpoint endpoint,
98                         ClientRuntime proxy)
99                 {
100                         throw new NotSupportedException ();
101                 }
102
103                 public void ApplyDispatchBehavior (
104                         ContractDescription description,
105                         ServiceEndpoint endpoint,
106                         DispatchRuntime dispatch)
107                 {
108                         throw new NotSupportedException ();
109                 }
110
111                 public void Validate (
112                         ContractDescription description,
113                         ServiceEndpoint endpoint)
114                 {
115                         throw new NotSupportedException ();
116                 }
117
118                 // IServiceContractGenerationExtensions
119
120                 public void GenerateContract (
121                         ServiceContractGenerationContext context)
122                 {
123                         this.context = context;
124                         ml_context.Contract = this;
125                 }
126
127                 ServiceContractGenerationContext context;
128
129                 public void Fixup ()
130                 {
131                         ContractDescription cd = context.Contract;
132                         ml_context.FindClientType (context);
133                         var parentClass = ml_context.ClientType;
134
135                         if (!generate_sync)
136                                 EliminateSync ();
137
138                         string name = cd.Name + "Channel";
139                         if (name [0] == 'I')
140                                 name = name.Substring (1);
141
142                         var gt = new CodeTypeReference (cd.Name);
143                         var clientBaseType = new CodeTypeReference ("System.ServiceModel.ClientBase", gt);
144                         // this omits namespace, but should compile
145                         var channelBase = new CodeTypeReference ("ChannelBase", gt);
146                         var type = new CodeTypeDeclaration (name);
147                         parentClass.Members.Add (type);
148                         type.BaseTypes.Add (channelBase);
149                         type.BaseTypes.Add (new CodeTypeReference (cd.Name));
150                         type.TypeAttributes |= TypeAttributes.NestedPrivate;
151
152                         ml_context.ChannelType = type;
153
154                         // .ctor(ClientBase<T> client)
155                         var ctor = new CodeConstructor ();
156                         ctor.Attributes = MemberAttributes.Public;
157                         ctor.Parameters.Add (
158                                 new CodeParameterDeclarationExpression (
159                                         clientBaseType, "client"));
160                         ctor.BaseConstructorArgs.Add (
161                                 new CodeArgumentReferenceExpression ("client"));
162                         type.Members.Add (ctor);
163
164                         // In Client type:
165                         // protected override TChannel CreateChannel()
166                         var creator = new CodeMemberMethod ();
167                         creator.Name = "CreateChannel";
168                         creator.Attributes = MemberAttributes.Family | MemberAttributes.Override;
169                         creator.ReturnType = gt;
170                         creator.Statements.Add (
171                                 new CodeMethodReturnStatement (
172                                         new CodeCastExpression (
173                                                 gt,
174                                                 new CodeObjectCreateExpression (
175                                                         new CodeTypeReference (name),
176                                                         new CodeThisReferenceExpression ()))));
177                         parentClass.Members.Add (creator);
178
179                         // clear IExtensibleDataObject. Since there is *no* way 
180                         // to identify the type of a TypeReference, I cannot do 
181                         // anything but this brutal removal.
182                         // Also clear ExtensionDataObject members.
183                         foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces) {
184                                 foreach (CodeTypeDeclaration ct in cns.Types) {
185                                         if (!ShouldPreserveBaseTypes (ct))
186                                                 ct.BaseTypes.Clear ();
187                                         CodeTypeMember cp = null, cf = null;
188                                         foreach (CodeTypeMember cm in ct.Members) {
189                                                 if (cm is CodeMemberProperty && cm.Name == "ExtensionData")
190                                                         cp = cm;
191                                                 else if (cm is CodeMemberField && cm.Name == "extensionDataField")
192                                                         cf = cm;
193                                         }
194                                         if (cf != null)
195                                                 ct.Members.Remove (cf);
196                                         if (cp != null)
197                                                 ct.Members.Remove (cp);
198                                 }
199                         }
200                 }
201
202                 bool ShouldPreserveBaseTypes (CodeTypeDeclaration ct)
203                 {
204                         foreach (CodeTypeReference cr in ct.BaseTypes) {
205                                 if (cr.BaseType == "System.ServiceModel.ClientBase`1")
206                                         return true;
207                                 if (cr.BaseType == "System.ComponentModel.AsyncCompletedEventArgs")
208                                         return true;
209                         }
210                         return false;
211                 }
212
213                 void EliminateSync ()
214                 {
215                         var type = context.ContractType;
216
217                         // remove such OperationContract methods that do not have AsyncPattern parameter. It is sort of hack as it does not check the value (it might be "false").
218                         var l = new List<CodeMemberMethod> ();
219                         foreach (CodeMemberMethod cm in type.Members) {
220                                 bool isOperation = false, isAsync = false;
221                                 foreach (CodeAttributeDeclaration att in cm.CustomAttributes) {
222                                         if (att.Name == "System.ServiceModel.OperationContractAttribute")
223                                                 isOperation = true;
224                                         else
225                                                 continue;
226                                         foreach (CodeAttributeArgument aa in att.Arguments) {
227                                                 if (aa.Name == "AsyncPattern") {
228                                                         isAsync = true;
229                                                         break;
230                                                 }
231                                         }
232                                         if (isAsync)
233                                                 break;
234                                 }
235                                 if (isOperation && !isAsync)
236                                         l.Add (cm);
237                         }
238                         foreach (var cm in l)
239                                 type.Members.Remove (cm);
240
241                         // remove corresponding client implementation methods. 
242                         // It is sort of hack as it only checks method and 
243                         // parameter names (ideally we want to check parameter
244                         // types, but there is no way to compare 
245                         // CodeTypeReferences).
246                         var lc = new List<CodeMemberMethod> ();
247                         foreach (var cm_ in ml_context.ClientType.Members) {
248                                 var cm = cm_ as CodeMemberMethod;
249                                 if (cm == null)
250                                         continue;
251                                 foreach (var sm in l) {
252                                         if (cm.Name != sm.Name || cm.Parameters.Count != sm.Parameters.Count)
253                                                 continue;
254                                         bool diff = false;
255                                         for (int i = 0; i < cm.Parameters.Count; i++) {
256                                                 var cp = cm.Parameters [i];
257                                                 var sp = sm.Parameters [i];
258                                                 if (cp.Direction != sp.Direction || cp.Name != sp.Name) {
259                                                         diff = true;
260                                                         break;
261                                                 }
262                                         }
263                                         if (diff)
264                                                 continue;
265                                         lc.Add (cm);
266                                         break;
267                                 }
268                         }
269                         foreach (var cm in lc)
270                                 ml_context.ClientType.Members.Remove (cm);
271                 }
272         }
273
274         class MoonlightChannelBaseOperationExtension : IOperationBehavior, IOperationContractGenerationExtension
275         {
276                 public MoonlightChannelBaseOperationExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
277                 {
278                         ml_context = mlContext;
279                         generate_sync = generateSync;
280                 }
281
282                 MoonlightChannelBaseContext ml_context;
283                 bool generate_sync;
284
285                 // IOperationBehavior
286
287                 public void AddBindingParameters (
288                         OperationDescription description,
289                         BindingParameterCollection parameters)
290                 {
291                         throw new NotSupportedException ();
292                 }
293
294                 public void ApplyDispatchBehavior (
295                         OperationDescription description,
296                         DispatchOperation dispatch)
297                 {
298                         throw new NotSupportedException ();
299                 }
300
301                 public void ApplyClientBehavior (
302                         OperationDescription description,
303                         ClientOperation proxy)
304                 {
305                         throw new NotSupportedException ();
306                 }
307
308                 public void Validate (
309                         OperationDescription description)
310                 {
311                         throw new NotSupportedException ();
312                 }
313
314                 // IOperationContractGenerationContext
315
316                 public void GenerateOperation (OperationContractGenerationContext context)
317                 {
318                         this.context = context;
319                         ml_context.Operations.Add (this);
320                 }
321
322                 OperationContractGenerationContext context;
323
324                 public void Fixup ()
325                 {
326                         if (generate_sync)
327                                 FixupSync ();
328                         FixupAsync ();
329                 }
330
331                 void FixupSync ()
332                 {
333                         var type = ml_context.ChannelType;
334                         var od = context.Operation;
335
336                         // sync method implementation
337                         CodeMemberMethod cm = new CodeMemberMethod ();
338                         type.Members.Add (cm);
339                         cm.Name = od.Name;
340                         cm.Attributes = MemberAttributes.Public 
341                                         | MemberAttributes.Final;
342
343                         var inArgs = new List<CodeParameterDeclarationExpression > ();
344                         var outArgs = new List<CodeParameterDeclarationExpression > ();
345
346                         foreach (CodeParameterDeclarationExpression p in context.SyncMethod.Parameters) {
347                                 inArgs.Add (p);
348                                 cm.Parameters.Add (p);
349                         }
350
351                         cm.ReturnType = context.SyncMethod.ReturnType;
352
353                         var argsDecl = new CodeVariableDeclarationStatement (
354                                 typeof (object []),
355                                 "args",
356                                 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
357                         cm.Statements.Add (argsDecl);
358
359                         var args = new List<CodeExpression> ();
360                         args.Add (new CodePrimitiveExpression (od.Name));
361                         args.Add (new CodeVariableReferenceExpression ("args"));
362
363                         CodeExpression call = new CodeMethodInvokeExpression (
364                                 new CodeBaseReferenceExpression (),
365                                 "Invoke",
366                                 args.ToArray ());
367
368                         if (cm.ReturnType.BaseType == "System.Void")
369                                 cm.Statements.Add (new CodeExpressionStatement (call));
370                         else
371                                 cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.SyncMethod.ReturnType, call)));
372                 }
373
374                 public void FixupAsync ()
375                 {
376                         var type = ml_context.ChannelType;
377                         var od = context.Operation;
378
379                         var baseExpr = new CodeBaseReferenceExpression ();
380                         var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
381
382                         // BeginXxx() implementation
383                         CodeMemberMethod cm = new CodeMemberMethod () {
384                                 Name = "Begin" + od.Name,
385                                 Attributes = MemberAttributes.Public | MemberAttributes.Final,
386                                 ReturnType = asyncResultType
387                                 };
388                         type.Members.Add (cm);
389
390                         var inArgs = new List<CodeParameterDeclarationExpression > ();
391                         foreach (CodeParameterDeclarationExpression p in context.BeginMethod.Parameters) {
392                                 inArgs.Add (p);
393                                 cm.Parameters.Add (p);
394                         }
395                         inArgs.RemoveAt (inArgs.Count - 1);
396                         inArgs.RemoveAt (inArgs.Count - 1);
397
398                         var call = new CodeMethodInvokeExpression (
399                                 baseExpr,
400                                 "BeginInvoke",
401                                 new CodePrimitiveExpression (od.Name),
402                                 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()),
403                                 new CodeArgumentReferenceExpression ("asyncCallback"),
404                                 new CodeArgumentReferenceExpression ("userState"));
405                         cm.Statements.Add (new CodeMethodReturnStatement (call));
406
407                         // EndXxx() implementation
408
409                         cm = new CodeMemberMethod () {
410                                 Name = "End" + od.Name,
411                                 Attributes = MemberAttributes.Public | MemberAttributes.Final,
412                                 ReturnType = context.EndMethod.ReturnType };
413                         type.Members.Add (cm);
414
415                         AddMethodParam (cm, typeof (IAsyncResult), "result");
416
417                         var outArgs = new List<CodeParameterDeclarationExpression > ();
418
419                         string resultArgName = "result";
420                         var argsDecl = new CodeVariableDeclarationStatement (
421                                 typeof (object []),
422                                 "args",
423                                 new CodeArrayCreateExpression (typeof (object), new CodePrimitiveExpression (outArgs.Count)));
424                         cm.Statements.Add (argsDecl);
425
426                         var ret = new CodeMethodInvokeExpression (
427                                 baseExpr,
428                                 "EndInvoke",
429                                 new CodePrimitiveExpression (od.Name),
430                                 new CodeVariableReferenceExpression ("args"),
431                                 new CodeArgumentReferenceExpression (resultArgName));
432                         if (cm.ReturnType.BaseType == "System.Void")
433                                 cm.Statements.Add (new CodeExpressionStatement (ret));
434                         else
435                                 cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.EndMethod.ReturnType, ret)));
436                 }
437
438                 void AddMethodParam (CodeMemberMethod cm, Type type, string name)
439                 {
440                         cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));
441                 }
442         }
443 }