Requires gmcs
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientProxyGenerator.cs
1 //
2 // ClientProxyGenerator.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006 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.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;
36
37 namespace System.ServiceModel
38 {
39         internal class ClientProxyGenerator : ProxyGeneratorBase
40         {
41                 public static Type CreateProxyType (Type contractInterface, ContractDescription cd, bool duplex)
42                 {
43                         string modname = "dummy";
44                         Type crtype =
45 #if !NET_2_1
46                                 duplex ? typeof (DuplexClientRuntimeChannel) :
47 #endif
48                                 typeof (ClientRuntimeChannel);
49
50                         // public class __clientproxy_MyContract : ClientRuntimeChannel, [ContractType]
51                         CodeClass c = new CodeModule (modname).CreateClass (
52                                 "__clientproxy_" + cd.Name,
53                                 crtype,
54                                 new Type [] {contractInterface});
55
56                         //
57                         // public __clientproxy_MyContract (
58                         //      ServiceEndpoint arg1, ChannelFactory arg2, EndpointAddress arg3, Uri arg4)
59                         //      : base (arg1, arg2, arg3, arg4)
60                         // {
61                         // }
62                         //
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.");
70                         b.Call (
71                                 ctor.GetThis (),
72                                 baseCtor,
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);
78                 }
79         }
80
81         internal class ProxyGeneratorBase
82         {
83                 protected static Type CreateProxyTypeOperations (Type crtype, CodeClass c, ContractDescription cd)
84                 {
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.
89 #if !NET_2_1
90                                 if (od.SyncMethod != null)
91                                         GenerateMethodImpl (c, crtype.GetMethod ("Process", bf), od.Name, od.SyncMethod);
92 #endif
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);
97                         }
98
99                         //Type zzz = c.CreateType ();
100                         //((System.Reflection.Emit.AssemblyBuilder) zzz.Assembly).Save (modname + ".dll");
101                         //return zzz;
102                         return c.CreateType ();
103                 }
104
105                 static void GenerateMethodImpl (CodeClass c, MethodInfo processMethod, string name, MethodInfo mi)
106                 {
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;
112                         // ...
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;
118                         b.Assign (paramsRef,
119                                   new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length)));
120                         for (int i = 0; i < pinfos.Length; i++) {
121                                 ParameterInfo par = pinfos [i];
122                                 if (!par.IsOut)
123                                         b.Assign (
124                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
125                                                 new CodeCast (typeof (object),
126                                                         new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
127                         }
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);
133                         else {
134                                 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
135                                 b.CurrentBlock.Add (retValueDecl);
136                                 retValue = retValueDecl.Variable;
137                                 b.Assign (retValue,
138                                         new CodeCast (mi.ReturnType,
139                                                 b.CallFunc (m.GetThis (), processMethod, argMethodInfo, argOperName, paramsRef)));
140                         }
141                         for (int i = 0; i < pinfos.Length; i++) {
142                                 ParameterInfo par = pinfos [i];
143                                 if (par.IsOut || par.ParameterType.IsByRef)
144                                         b.Assign (
145                                                 new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i),
146                                                 new CodeCast (par.ParameterType.GetElementType (),
147                                                         new CodeArrayItem (paramsRef, new CodeLiteral (i))));
148                         }
149                         if (retValue != null)
150                                 b.Return (retValue);
151                 }
152
153                 static void GenerateBeginMethodImpl (CodeClass c, MethodInfo beginProcessMethod, string name, MethodInfo mi)
154                 {
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;
160                         // ...
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;
166                         b.Assign (paramsRef,
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];
170                                 if (!par.IsOut)
171                                         b.Assign (
172                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
173                                                 new CodeCast (typeof (object), m.GetArg (i + 1)));
174                         }
175                         CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
176                         CodeLiteral argOperName = new CodeLiteral (name);
177
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);
182
183                         CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
184                         b.CurrentBlock.Add (retValueDecl);
185                         CodeVariableReference retValue = retValueDecl.Variable;
186                         b.Assign (retValue,
187                                 new CodeCast (mi.ReturnType,
188                                         b.CallFunc (m.GetThis (), beginProcessMethod, argMethodInfo, argOperName, paramsRef, callbackRef, stateRef)));
189
190                         b.Return (retValue);
191                 }
192
193                 static void GenerateEndMethodImpl (CodeClass c, MethodInfo endProcessMethod, string name, MethodInfo mi)
194                 {
195                         CodeMethod m = c.ImplementMethod (mi);
196                         CodeBuilder b = m.CodeBuilder;
197                         ParameterInfo [] pinfos = mi.GetParameters ();
198
199                         ParameterInfo p = pinfos [0];
200                         CodeArgumentReference asyncResultRef = m.GetArg (0);
201                         
202                         CodeVariableDeclaration paramsDecl = new CodeVariableDeclaration (typeof (object []), "parameters");
203                         b.CurrentBlock.Add (paramsDecl);
204                         CodeVariableReference paramsRef = paramsDecl.Variable;
205                         b.Assign (paramsRef,
206                                   new CodeNewArray (typeof (object), new CodeLiteral (pinfos.Length - 1)));
207                         /*
208                         for (int i = 0; i < pinfos.Length - 2; i++) {
209                                 ParameterInfo par = pinfos [i];
210                                 if (!par.IsOut)
211                                         b.Assign (
212                                                 new CodeArrayItem (paramsRef, new CodeLiteral (i)),
213                                                 new CodeCast (typeof (object),
214                                                         new CodeArgumentReference (par.ParameterType, par.Position + 1, "arg" + i)));
215                         }
216                         */
217                         CodeMethodCall argMethodInfo = new CodeMethodCall (typeof (MethodBase), "GetCurrentMethod");
218                         CodeLiteral argOperName = new CodeLiteral (name);
219                         
220                         CodeVariableReference retValue = null;
221                         if (mi.ReturnType == typeof (void))
222                                 // FIXME: pass appropriate argument.
223                                 b.Call (m.GetThis (), endProcessMethod, argOperName, paramsRef, asyncResultRef);
224                         else {
225                                 CodeVariableDeclaration retValueDecl = new CodeVariableDeclaration (mi.ReturnType, "retValue");
226                                 b.CurrentBlock.Add (retValueDecl);
227                                 retValue = retValueDecl.Variable;
228                                 // FIXME: pass appropriate argument.
229                                 b.Assign (retValue,
230                                         new CodeCast (mi.ReturnType,
231                                                 b.CallFunc (m.GetThis (), endProcessMethod, argOperName, paramsRef, asyncResultRef)));
232                         }
233                         if (retValue != null)
234                                 b.Return (retValue);
235                 }
236         }
237 }