ongoing XmlSerializerMessageContractImporter work.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientProxyGenerator.cs
1 #if DISABLE_REAL_PROXY
2 //
3 // ClientProxyGenerator.cs
4 //
5 // Author:
6 //      Atsushi Enomoto <atsushi@ximian.com>
7 //
8 // Copyright (C) 2006 Novell, Inc.  http://www.novell.com
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 #if !MONOTOUCH
30 using System;
31 using System.Collections.Generic;
32 using System.Reflection;
33 using System.ServiceModel;
34 using System.ServiceModel.Channels;
35 using System.ServiceModel.Description;
36 using System.ServiceModel.Dispatcher;
37 using Mono.CodeGeneration;
38 using System.ServiceModel.MonoInternal;
39
40 namespace System.ServiceModel
41 {
42         internal class ClientProxyKey {
43                 Type contractInterface;
44                 ContractDescription cd;
45                 bool duplex;
46
47                 public ClientProxyKey (Type contractInterface, ContractDescription cd, bool duplex) {
48                         this.contractInterface = contractInterface;
49                         this.cd = cd;
50                         this.duplex = duplex;
51                 }
52
53
54                 public override int GetHashCode () {
55                         return contractInterface.GetHashCode () ^ cd.GetHashCode ();
56                 }
57
58                 public override bool Equals (object o) {
59                         ClientProxyKey key = o as ClientProxyKey;
60                         if (key == null)
61                                 return false;
62                         return contractInterface == key.contractInterface && cd == key.cd && duplex == key.duplex;
63                 }
64         }
65
66         internal class ClientProxyGenerator : ProxyGeneratorBase
67         {
68                 static Dictionary<ClientProxyKey, Type> proxy_cache = new Dictionary<ClientProxyKey, Type> ();
69
70
71                 public static Type CreateProxyType (Type requestedType, ContractDescription cd, bool duplex)
72                 {
73                         ClientProxyKey key = new ClientProxyKey (requestedType, cd, duplex);
74                         Type res;
75                         lock (proxy_cache) {
76                                 if (proxy_cache.TryGetValue (key, out res))
77                                         return res;
78                         }
79
80                         string modname = "dummy";
81                         Type crtype =
82 #if !NET_2_1
83                                 duplex ? typeof (DuplexClientRuntimeChannel) :
84 #endif
85                                 typeof (ClientRuntimeChannel);
86
87                         // public class __clientproxy_MyContract : (Duplex)ClientRuntimeChannel, [ContractType]
88                         var types = new List<Type> ();
89                         types.Add (requestedType);
90                         if (!cd.ContractType.IsAssignableFrom (requestedType))
91                                 types.Add (cd.ContractType);
92                         if (cd.CallbackContractType != null && !cd.CallbackContractType.IsAssignableFrom (requestedType))
93                                 types.Add (cd.CallbackContractType);
94                         CodeClass c = new CodeModule (modname).CreateClass ("__clientproxy_" + cd.Name, crtype, types.ToArray ());
95
96                         //
97                         // public __clientproxy_MyContract (
98                         //      ServiceEndpoint arg1, ChannelFactory arg2, EndpointAddress arg3, Uri arg4)
99                         //      : base (arg1, arg2, arg3, arg4)
100                         // {
101                         // }
102                         //
103                         Type [] ctorargs = new Type [] {typeof (ServiceEndpoint), typeof (ChannelFactory), typeof (EndpointAddress), typeof (Uri)};
104                         CodeMethod ctor = c.CreateConstructor (
105                                 MethodAttributes.Public, ctorargs);
106                         CodeBuilder b = ctor.CodeBuilder;
107                         MethodBase baseCtor = crtype.GetConstructors (
108                                 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) [0];
109                         if (baseCtor == null) throw new Exception ("INTERNAL ERROR: ClientRuntimeChannel.ctor() was not found.");
110                         b.Call (
111                                 ctor.GetThis (),
112                                 baseCtor,
113                                 new CodeArgumentReference (typeof (ServiceEndpoint), 1, "arg0"),
114                                 new CodeArgumentReference (typeof (ChannelFactory), 2, "arg1"),
115                                 new CodeArgumentReference (typeof (EndpointAddress), 3, "arg2"),
116                                 new CodeArgumentReference (typeof (Uri), 4, "arg3"));
117                         res = CreateProxyTypeOperations (crtype, c, cd);
118
119                         lock (proxy_cache) {
120                                 proxy_cache [key] = res;
121                         }
122                         return res;
123                 }
124         }
125
126         internal class ProxyGeneratorBase
127         {
128                 protected static Type CreateProxyTypeOperations (Type crtype, CodeClass c, ContractDescription cd)
129                 {
130                         // member implementation
131                         BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
132                         foreach (OperationDescription od in cd.Operations) {
133                                 // FIXME: handle properties and events.
134 #if !NET_2_1
135                                 if (od.SyncMethod != null)
136                                         GenerateMethodImpl (c, crtype.GetMethod ("Process", bf), od.Name, od.SyncMethod);
137 #endif
138                                 if (od.BeginMethod != null)
139                                         GenerateBeginMethodImpl (c, crtype.GetMethod ("BeginProcess", bf), od.Name, od.BeginMethod);
140                                 if (od.EndMethod != null)
141                                         GenerateEndMethodImpl (c, crtype.GetMethod ("EndProcess", bf), od.Name, od.EndMethod);
142                         }
143
144                         Type ret = c.CreateType ();
145                         return ret;
146                 }
147
148                 static void GenerateMethodImpl (CodeClass c, MethodInfo processMethod, string name, MethodInfo mi)
149                 {
150                         CodeMethod m = c.ImplementMethod (mi);
151                         CodeBuilder b = m.CodeBuilder;
152                         // object [] parameters = new object [x];
153                         // parameters [0] = arg1;
154                         // parameters [1] = arg2;
155                         // ...
156                         // (return) Process (Contract.Operations [operName].SyncMethod, operName, parameters);
157                         ParameterInfo [] pinfos = mi.GetParameters ();
158                         CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
159                         b.CurrentBlock.Add (paramsDecl);
160                         CodeVariableReference paramsRef = paramsDecl.Variable;
161                         b.Assign (paramsRef,
162                                   new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length)));
163                         for (int i = 0; i < pinfos.Length; i++) {
164                                 ParameterInfo par = pinfos [i];
165                                 if (!par.IsOut)
166                                         b.Assign (
167                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
168                                                 new CodeCast (typeof (object),
169                                                         new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
170                         }
171 #if USE_OD_REFERENCE_IN_PROXY
172                         CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "SyncMethod");
173 #else
174                         CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
175 #endif
176                         CodeLiteral argOperName = new CodeLiteral (name);
177                         CodeVariableReference retValue = null;
178                         if (mi.ReturnType == typeof (void))
179                                 b.Call (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef);
180                         else {
181                                 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
182                                 b.CurrentBlock.Add (retValueDecl);
183                                 retValue = retValueDecl.Variable;
184                                 b.Assign (retValue,
185                                         new CodeCast (mi.ReturnType,
186                                                 b.CallFunc (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef)));
187                         }
188                         for (int i = 0; i < pinfos.Length; i++) {
189                                 ParameterInfo par = pinfos [i];
190                                 if (par.IsOut || par.ParameterType.IsByRef)
191                                         b.Assign (
192                                                 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i),
193                                                 new CodeCast (par.ParameterType.GetElementType (),
194                                                         new CodeArrayItem (paramsRef, new CodeLiteral (i))));
195                         }
196                         if (retValue != null)
197                                 b.Return (retValue);
198                 }
199
200                 static CodePropertyReference GetOperationMethod (CodeMethod m, CodeBuilder b, string name, string methodPropertyName)
201                 {
202                         return new CodePropertyReference (
203                                 b.CallFunc (
204                                         // this.Contract.Operations
205                                         new CodePropertyReference (
206                                                 new CodePropertyReference (
207                                                         m.GetThis (),
208                                                         typeof (ClientRuntimeChannel).GetProperty ("Contract")),
209                                                 typeof (ContractDescription).GetProperty ("Operations")),
210                                         // .Find (name)
211                                         typeof (OperationDescriptionCollection).GetMethod ("Find"),
212                                         new CodeLiteral (name)),
213                                 // .SyncMethod
214                                 typeof (OperationDescription).GetProperty (methodPropertyName));
215                 }
216
217                 static void GenerateBeginMethodImpl (CodeClass c, MethodInfo beginProcessMethod, string name, MethodInfo mi)
218                 {
219                         CodeMethod m = c.ImplementMethod (mi);
220                         CodeBuilder b = m.CodeBuilder;
221                         // object [] parameters = new object [x];
222                         // parameters [0] = arg1;
223                         // parameters [1] = arg2;
224                         // ...
225                         // (return) BeginProcess (Contract.Operations [operName].BeginMethod, operName, parameters, asyncCallback, userState);
226                         ParameterInfo [] pinfos = mi.GetParameters ();
227                         CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
228                         b.CurrentBlock.Add (paramsDecl);
229                         CodeVariableReference paramsRef = paramsDecl.Variable;
230                         b.Assign (paramsRef,
231                                   new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 2)));
232                         for (int i = 0; i < pinfos.Length - 2; i++) {
233                                 ParameterInfo par = pinfos [i];
234                                 if (!par.IsOut)
235                                         b.Assign (
236                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
237                                                 new CodeCast (typeof (object), m.GetArg (i)));
238                         }
239 #if USE_OD_REFERENCE_IN_PROXY
240                         CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "BeginMethod");
241 #else
242                         CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
243 #endif
244                         CodeLiteral argOperName = new CodeLiteral (name);
245
246                         ParameterInfo p = pinfos [pinfos.Length - 2];
247                         CodeArgumentReference callbackRef = new CodeArgumentReference (typeof (AsyncCallback), p.Position + 1, p.Name);
248                         p = pinfos [pinfos.Length - 1];
249                         CodeArgumentReference stateRef = new CodeArgumentReference (typeof (object), p.Position + 1, p.Name);
250
251                         CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
252                         b.CurrentBlock.Add (retValueDecl);
253                         CodeVariableReference retValue = retValueDecl.Variable;
254                         b.Assign (retValue,
255                                 new CodeCast (mi.ReturnType,
256                                         b.CallFunc (m.GetThis (), beginProcessMethod, argMethodInfo, argOperName, paramsRef, callbackRef, stateRef)));
257
258                         b.Return (retValue);
259                 }
260
261                 static void GenerateEndMethodImpl (CodeClass c, MethodInfo endProcessMethod, string name, MethodInfo mi)
262                 {
263                         CodeMethod m = c.ImplementMethod (mi);
264                         CodeBuilder b = m.CodeBuilder;
265                         ParameterInfo [] pinfos = mi.GetParameters ();
266
267                         ParameterInfo p = pinfos [0];
268                         CodeArgumentReference asyncResultRef = m.GetArg (0);
269                         
270                         CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
271                         b.CurrentBlock.Add (paramsDecl);
272                         CodeVariableReference paramsRef = paramsDecl.Variable;
273                         b.Assign (paramsRef,
274                                   new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 1)));
275                         /*
276                         for (int i = 0; i < pinfos.Length - 2; i++) {
277                                 ParameterInfo par = pinfos [i];
278                                 if (!par.IsOut)
279                                         b.Assign (
280                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
281                                                 new CodeCast (typeof (object),
282                                                         new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
283                         }
284                         */
285 #if USE_OD_REFERENCE_IN_PROXY
286                         CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "EndMethod");
287 #else
288                         CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
289 #endif
290                         CodeLiteral argOperName = new CodeLiteral (name);
291                         
292                         CodeVariableReference retValue = null;
293                         if (mi.ReturnType == typeof (void))
294                                 b.Call (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef);
295                         else {
296                                 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
297                                 b.CurrentBlock.Add (retValueDecl);
298                                 retValue = retValueDecl.Variable;
299                                 b.Assign (retValue,
300                                         new CodeCast (mi.ReturnType,
301                                                 b.CallFunc (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef)));
302                         }
303                         // FIXME: fill out parameters
304                         if (retValue != null)
305                                 b.Return (retValue);
306                 }
307         }
308 }
309 #endif
310
311 #endif