3 // ClientProxyGenerator.cs
6 // Atsushi Enomoto <atsushi@ximian.com>
8 // Copyright (C) 2006 Novell, Inc. http://www.novell.com
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
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;
40 namespace System.ServiceModel
42 internal class ClientProxyKey {
43 Type contractInterface;
44 ContractDescription cd;
47 public ClientProxyKey (Type contractInterface, ContractDescription cd, bool duplex) {
48 this.contractInterface = contractInterface;
54 public override int GetHashCode () {
55 return contractInterface.GetHashCode () ^ cd.GetHashCode ();
58 public override bool Equals (object o) {
59 ClientProxyKey key = o as ClientProxyKey;
62 return contractInterface == key.contractInterface && cd == key.cd && duplex == key.duplex;
66 internal class ClientProxyGenerator : ProxyGeneratorBase
68 static Dictionary<ClientProxyKey, Type> proxy_cache = new Dictionary<ClientProxyKey, Type> ();
71 public static Type CreateProxyType (Type requestedType, ContractDescription cd, bool duplex)
73 ClientProxyKey key = new ClientProxyKey (requestedType, cd, duplex);
76 if (proxy_cache.TryGetValue (key, out res))
80 string modname = "dummy";
83 duplex ? typeof (DuplexClientRuntimeChannel) :
85 typeof (ClientRuntimeChannel);
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 ());
97 // public __clientproxy_MyContract (
98 // ServiceEndpoint arg1, ChannelFactory arg2, EndpointAddress arg3, Uri arg4)
99 // : base (arg1, arg2, arg3, arg4)
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.");
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);
120 proxy_cache [key] = res;
126 internal class ProxyGeneratorBase
128 protected static Type CreateProxyTypeOperations (Type crtype, CodeClass c, ContractDescription cd)
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.
135 if (od.SyncMethod != null)
136 GenerateMethodImpl (c, crtype.GetMethod ("Process", bf), od.Name, od.SyncMethod);
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);
144 Type ret = c.CreateType ();
148 static void GenerateMethodImpl (CodeClass c, MethodInfo processMethod, string name, MethodInfo mi)
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;
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;
162 new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length)));
163 for (int i = 0; i < pinfos.Length; i++) {
164 ParameterInfo par = pinfos [i];
167 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
168 new CodeCast (typeof (object),
169 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
171 #if USE_OD_REFERENCE_IN_PROXY
172 CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "SyncMethod");
174 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
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);
181 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
182 b.CurrentBlock.Add (retValueDecl);
183 retValue = retValueDecl.Variable;
185 new CodeCast (mi.ReturnType,
186 b.CallFunc (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef)));
188 for (int i = 0; i < pinfos.Length; i++) {
189 ParameterInfo par = pinfos [i];
190 if (par.IsOut || par.ParameterType.IsByRef)
192 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i),
193 new CodeCast (par.ParameterType.GetElementType (),
194 new CodeArrayItem (paramsRef, new CodeLiteral (i))));
196 if (retValue != null)
200 static CodePropertyReference GetOperationMethod (CodeMethod m, CodeBuilder b, string name, string methodPropertyName)
202 return new CodePropertyReference (
204 // this.Contract.Operations
205 new CodePropertyReference (
206 new CodePropertyReference (
208 typeof (ClientRuntimeChannel).GetProperty ("Contract")),
209 typeof (ContractDescription).GetProperty ("Operations")),
211 typeof (OperationDescriptionCollection).GetMethod ("Find"),
212 new CodeLiteral (name)),
214 typeof (OperationDescription).GetProperty (methodPropertyName));
217 static void GenerateBeginMethodImpl (CodeClass c, MethodInfo beginProcessMethod, string name, MethodInfo mi)
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;
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;
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];
236 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
237 new CodeCast (typeof (object), m.GetArg (i)));
239 #if USE_OD_REFERENCE_IN_PROXY
240 CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "BeginMethod");
242 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
244 CodeLiteral argOperName = new CodeLiteral (name);
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);
251 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
252 b.CurrentBlock.Add (retValueDecl);
253 CodeVariableReference retValue = retValueDecl.Variable;
255 new CodeCast (mi.ReturnType,
256 b.CallFunc (m.GetThis (), beginProcessMethod, argMethodInfo, argOperName, paramsRef, callbackRef, stateRef)));
261 static void GenerateEndMethodImpl (CodeClass c, MethodInfo endProcessMethod, string name, MethodInfo mi)
263 CodeMethod m = c.ImplementMethod (mi);
264 CodeBuilder b = m.CodeBuilder;
265 ParameterInfo [] pinfos = mi.GetParameters ();
267 ParameterInfo p = pinfos [0];
268 CodeArgumentReference asyncResultRef = m.GetArg (0);
270 CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
271 b.CurrentBlock.Add (paramsDecl);
272 CodeVariableReference paramsRef = paramsDecl.Variable;
274 new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 1)));
276 for (int i = 0; i < pinfos.Length - 2; i++) {
277 ParameterInfo par = pinfos [i];
280 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
281 new CodeCast (typeof (object),
282 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
285 #if USE_OD_REFERENCE_IN_PROXY
286 CodePropertyReference argMethodInfo = GetOperationMethod (m, b, name, "EndMethod");
288 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
290 CodeLiteral argOperName = new CodeLiteral (name);
292 CodeVariableReference retValue = null;
293 if (mi.ReturnType == typeof (void))
294 b.Call (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef);
296 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
297 b.CurrentBlock.Add (retValueDecl);
298 retValue = retValueDecl.Variable;
300 new CodeCast (mi.ReturnType,
301 b.CallFunc (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef)));
303 // FIXME: fill out parameters
304 if (retValue != null)