2 // ClientProxyGenerator.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2006 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.ServiceModel;
32 using System.ServiceModel.Channels;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using Mono.CodeGeneration;
37 namespace System.ServiceModel
39 internal class ClientProxyGenerator : ProxyGeneratorBase
41 public static Type CreateProxyType (Type contractInterface, ContractDescription cd, bool duplex)
43 string modname = "dummy";
46 duplex ? typeof (DuplexClientRuntimeChannel) :
48 typeof (ClientRuntimeChannel);
50 // public class __clientproxy_MyContract : ClientRuntimeChannel, [ContractType]
51 CodeClass c = new CodeModule (modname).CreateClass (
52 "__clientproxy_" + cd.Name,
54 new Type [] {contractInterface});
57 // public __clientproxy_MyContract (
58 // ServiceEndpoint arg1, ChannelFactory arg2, EndpointAddress arg3, Uri arg4)
59 // : base (arg1, arg2, arg3, arg4)
63 Type [] ctorargs = new Type [] {typeof (ServiceEndpoint), typeof (ChannelFactory), typeof (EndpointAddress), typeof (Uri)};
64 CodeMethod ctor = c.CreateConstructor (
65 MethodAttributes.Public, ctorargs);
66 CodeBuilder b = ctor.CodeBuilder;
67 MethodBase baseCtor = crtype.GetConstructors (
68 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) [0];
69 if (baseCtor == null) throw new Exception ("INTERNAL ERROR: ClientRuntimeChannel.ctor() was not found.");
73 new CodeArgumentReference (typeof (ServiceEndpoint), 1, "arg0"),
74 new CodeArgumentReference (typeof (ChannelFactory), 2, "arg1"),
75 new CodeArgumentReference (typeof (EndpointAddress), 3, "arg2"),
76 new CodeArgumentReference (typeof (Uri), 4, "arg3"));
77 return CreateProxyTypeOperations (crtype, c, cd);
81 internal class ProxyGeneratorBase
83 protected static Type CreateProxyTypeOperations (Type crtype, CodeClass c, ContractDescription cd)
85 // member implementation
86 BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
87 foreach (OperationDescription od in cd.Operations) {
88 // FIXME: handle properties and events.
90 if (od.SyncMethod != null)
91 GenerateMethodImpl (c, crtype.GetMethod ("Process", bf), od.Name, od.SyncMethod);
93 if (od.BeginMethod != null)
94 GenerateBeginMethodImpl (c, crtype.GetMethod ("BeginProcess", bf), od.Name, od.BeginMethod);
95 if (od.EndMethod != null)
96 GenerateEndMethodImpl (c, crtype.GetMethod ("EndProcess", bf), od.Name, od.EndMethod);
99 //Type zzz = c.CreateType ();
100 //((System.Reflection.Emit.AssemblyBuilder) zzz.Assembly).Save (modname + ".dll");
102 return c.CreateType ();
105 static void GenerateMethodImpl (CodeClass c, MethodInfo processMethod, string name, MethodInfo mi)
107 CodeMethod m = c.ImplementMethod (mi);
108 CodeBuilder b = m.CodeBuilder;
109 // object [] parameters = new object [x];
110 // parameters [0] = arg1;
111 // parameters [1] = arg2;
113 // (return) Process (MethodBase.GetCurrentMethod(), operName, parameters);
114 ParameterInfo [] pinfos = mi.GetParameters ();
115 CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
116 b.CurrentBlock.Add (paramsDecl);
117 CodeVariableReference paramsRef = paramsDecl.Variable;
119 new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length)));
120 for (int i = 0; i < pinfos.Length; i++) {
121 ParameterInfo par = pinfos [i];
124 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
125 new CodeCast (typeof (object),
126 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
128 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
129 CodeLiteral argOperName = new CodeLiteral (name);
130 CodeVariableReference retValue = null;
131 if (mi.ReturnType == typeof (void))
132 b.Call (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef);
134 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
135 b.CurrentBlock.Add (retValueDecl);
136 retValue = retValueDecl.Variable;
138 new CodeCast (mi.ReturnType,
139 b.CallFunc (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef)));
141 for (int i = 0; i < pinfos.Length; i++) {
142 ParameterInfo par = pinfos [i];
143 if (par.IsOut || par.ParameterType.IsByRef)
145 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i),
146 new CodeCast (par.ParameterType.GetElementType (),
147 new CodeArrayItem (paramsRef, new CodeLiteral (i))));
149 if (retValue != null)
153 static void GenerateBeginMethodImpl (CodeClass c, MethodInfo beginProcessMethod, string name, MethodInfo mi)
155 CodeMethod m = c.ImplementMethod (mi);
156 CodeBuilder b = m.CodeBuilder;
157 // object [] parameters = new object [x];
158 // parameters [0] = arg1;
159 // parameters [1] = arg2;
161 // (return) BeginProcess (MethodBase.GetCurrentMethod(), operName, parameters, asyncCallback, userState);
162 ParameterInfo [] pinfos = mi.GetParameters ();
163 CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
164 b.CurrentBlock.Add (paramsDecl);
165 CodeVariableReference paramsRef = paramsDecl.Variable;
167 new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 2)));
168 for (int i = 0; i < pinfos.Length - 2; i++) {
169 ParameterInfo par = pinfos [i];
172 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
173 new CodeCast (typeof (object), m.GetArg (i + 1)));
175 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
176 CodeLiteral argOperName = new CodeLiteral (name);
178 ParameterInfo p = pinfos [pinfos.Length - 2];
179 CodeArgumentReference callbackRef = new CodeArgumentReference (typeof (AsyncCallback), p.Position + 1, p.Name);
180 p = pinfos [pinfos.Length - 1];
181 CodeArgumentReference stateRef = new CodeArgumentReference (typeof (object), p.Position + 1, p.Name);
183 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
184 b.CurrentBlock.Add (retValueDecl);
185 CodeVariableReference retValue = retValueDecl.Variable;
187 new CodeCast (mi.ReturnType,
188 b.CallFunc (m.GetThis (), beginProcessMethod, argMethodInfo, argOperName, paramsRef, callbackRef, stateRef)));
193 static void GenerateEndMethodImpl (CodeClass c, MethodInfo endProcessMethod, string name, MethodInfo mi)
195 CodeMethod m = c.ImplementMethod (mi);
196 CodeBuilder b = m.CodeBuilder;
197 ParameterInfo [] pinfos = mi.GetParameters ();
199 ParameterInfo p = pinfos [0];
200 CodeArgumentReference asyncResultRef = m.GetArg (0);
202 CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
203 b.CurrentBlock.Add (paramsDecl);
204 CodeVariableReference paramsRef = paramsDecl.Variable;
206 new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 1)));
208 for (int i = 0; i < pinfos.Length - 2; i++) {
209 ParameterInfo par = pinfos [i];
212 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
213 new CodeCast (typeof (object),
214 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
217 CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
218 CodeLiteral argOperName = new CodeLiteral (name);
220 CodeVariableReference retValue = null;
221 if (mi.ReturnType == typeof (void))
222 b.Call (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef);
224 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
225 b.CurrentBlock.Add (retValueDecl);
226 retValue = retValueDecl.Variable;
228 new CodeCast (mi.ReturnType,
229 b.CallFunc (m.GetThis (), endProcessMethod, argMethodInfo, argOperName, paramsRef, asyncResultRef)));
231 // FIXME: fill out parameters
232 if (retValue != null)