Modified to support char type.
[platform/upstream/dbus.git] / mono / ProxyBuilder.cs
1 namespace DBus
2 {
3   using System;
4   using System.Runtime.InteropServices;
5   using System.Diagnostics;
6   using System.Collections;
7   using System.Threading;
8   using System.Reflection;
9   using System.Reflection.Emit;
10
11   internal class ProxyBuilder
12   {
13     private Service service= null;
14     private string pathName = null;
15     private Type type = null;
16     private Introspector introspector = null;
17     private static AssemblyBuilder proxyAssembly;
18     
19     private static MethodInfo Service_NameMI = typeof(Service).GetMethod("get_Name", 
20                                                                             new Type[0]);
21     private static MethodInfo Service_ConnectionMI = typeof(Service).GetMethod("get_Connection",
22                                                                                   new Type[0]);
23     private static MethodInfo Message_ArgumentsMI = typeof(Message).GetMethod("get_Arguments",
24                                                                                  new Type[0]);
25     private static MethodInfo Arguments_InitAppendingMI = typeof(Arguments).GetMethod("InitAppending",
26                                                                                           new Type[0]);
27     private static MethodInfo Arguments_AppendMI = typeof(Arguments).GetMethod("Append",
28                                                                                   new Type[] {typeof(DBusType.IDBusType)});
29     private static MethodInfo Message_SendWithReplyAndBlockMI = typeof(Message).GetMethod("SendWithReplyAndBlock",
30                                                                                              new Type[0]);
31     private static MethodInfo Arguments_GetEnumeratorMI = typeof(Arguments).GetMethod("GetEnumerator",
32                                                                                           new Type[0]);
33     private static MethodInfo IEnumerator_MoveNextMI = typeof(System.Collections.IEnumerator).GetMethod("MoveNext",
34                                                                                                         new Type[0]);
35     private static MethodInfo IEnumerator_CurrentMI = typeof(System.Collections.IEnumerator).GetMethod("get_Current",
36                                                                                                        new Type[0]);
37     private static MethodInfo Type_GetTypeFromHandleMI = typeof(System.Type).GetMethod("GetTypeFromHandle",
38                                                                                        new Type[] {typeof(System.RuntimeTypeHandle)});
39     private static MethodInfo IDBusType_GetMI = typeof(DBusType.IDBusType).GetMethod("Get",
40                                                                                      new Type[] {typeof(System.Type)});
41     private static ConstructorInfo MethodCall_C = typeof(MethodCall).GetConstructor(new Type[] {typeof(Service),
42                                                                                                 typeof(string),
43                                                                                                 typeof(string),
44                                                                                                 typeof(string)});
45     
46                                                                                         
47
48     public ProxyBuilder(Service service, Type type, string pathName)
49     {
50       this.service = service;
51       this.pathName = pathName;
52       this.type = type;
53       this.introspector = Introspector.GetIntrospector(type);
54     }
55
56     private void BuildMethod(MethodInfo method, 
57                              InterfaceProxy interfaceProxy,
58                              ref TypeBuilder typeB, 
59                              FieldInfo serviceF,
60                              FieldInfo pathF)
61     {
62       ParameterInfo[] pars = method.GetParameters();
63       Type[] parTypes = new Type[pars.Length];
64       for (int parN = 0; parN < pars.Length; parN++) {
65         parTypes[parN] = pars[parN].ParameterType;
66       }
67
68       // Generate the code
69       MethodBuilder methodBuilder = typeB.DefineMethod(method.Name, 
70                                                        MethodAttributes.Public |
71                                                        MethodAttributes.HideBySig |
72                                                        MethodAttributes.Virtual, 
73                                                        method.ReturnType, 
74                                                        parTypes);
75       ILGenerator generator = methodBuilder.GetILGenerator();
76
77       for (int parN = 0; parN < pars.Length; parN++) {
78         methodBuilder.DefineParameter(parN + 1, pars[parN].Attributes, pars[parN].Name);
79       }
80
81       // Generate the locals
82       LocalBuilder methodCallL = generator.DeclareLocal(typeof(MethodCall));
83       methodCallL.SetLocalSymInfo("methodCall");
84       LocalBuilder replyL = generator.DeclareLocal(typeof(MethodReturn));
85       replyL.SetLocalSymInfo("reply");
86       LocalBuilder enumeratorL = generator.DeclareLocal(typeof(System.Collections.IEnumerator));
87       enumeratorL.SetLocalSymInfo("enumerator");
88
89       if (method.ReturnType != typeof(void)) {
90         LocalBuilder retvalL = generator.DeclareLocal(method.ReturnType);
91         retvalL.SetLocalSymInfo("retval");
92       }
93
94       //generator.EmitWriteLine("MethodCall methodCall = new MethodCall(...)");
95       generator.Emit(OpCodes.Ldsfld, serviceF);
96       generator.Emit(OpCodes.Ldarg_0);
97       generator.Emit(OpCodes.Ldfld, pathF);
98       generator.Emit(OpCodes.Ldstr, interfaceProxy.InterfaceName);
99       generator.Emit(OpCodes.Ldstr, method.Name);
100       generator.Emit(OpCodes.Newobj, MethodCall_C);
101       generator.Emit(OpCodes.Stloc_0);
102
103       //generator.EmitWriteLine("methodCall.Arguments.InitAppending()");
104       generator.Emit(OpCodes.Ldloc_0);
105       generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
106       generator.EmitCall(OpCodes.Callvirt, Arguments_InitAppendingMI, null);
107
108       for (int parN = 0; parN < pars.Length; parN++) {
109         ParameterInfo par = pars[parN];
110         if (!par.IsOut) {
111           EmitIn(generator, par.ParameterType, parN, serviceF);
112         }
113       }
114       
115       //generator.EmitWriteLine("MethodReturn reply = methodCall.SendWithReplyAndBlock()");
116       generator.Emit(OpCodes.Ldloc_0);
117       generator.EmitCall(OpCodes.Callvirt, Message_SendWithReplyAndBlockMI, null);      
118       generator.Emit(OpCodes.Stloc_1);
119
120       //generator.EmitWriteLine("IEnumerator enumeartor = reply.Arguments.GetEnumerator()");
121       generator.Emit(OpCodes.Ldloc_1);
122       generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
123       generator.EmitCall(OpCodes.Callvirt, Arguments_GetEnumeratorMI, null);
124       generator.Emit(OpCodes.Stloc_2);
125
126       // handle the return value
127       if (method.ReturnType != typeof(void)) {
128         EmitOut(generator, method.ReturnType, 0);
129       }
130
131       for (int parN = 0; parN < pars.Length; parN++) {
132         ParameterInfo par = pars[parN];
133         if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) {
134           EmitOut(generator, par.ParameterType, parN);
135         }
136       }
137
138       if (method.ReturnType != typeof(void)) {
139         generator.Emit(OpCodes.Ldloc_3);
140       }
141       
142       generator.Emit(OpCodes.Ret);
143
144       // Generate the method
145       typeB.DefineMethodOverride(methodBuilder, method);
146     }
147
148     private void EmitIn(ILGenerator generator, Type parType, int parN, FieldInfo serviceF)
149     {
150       Type inParType = Arguments.MatchType(parType);
151       //generator.EmitWriteLine("methodCall.Arguments.Append(...)");
152       generator.Emit(OpCodes.Ldloc_0);
153       generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
154       generator.Emit(OpCodes.Ldarg_S, parN + 1);
155
156       // Call the DBusType EmitMarshalIn to make it emit itself
157       object[] pars = new object[] {generator, parType};
158       inParType.InvokeMember("EmitMarshalIn", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
159
160       generator.Emit(OpCodes.Ldsfld, serviceF);
161       generator.Emit(OpCodes.Newobj, Arguments.GetDBusTypeConstructor(inParType, parType));
162       generator.EmitCall(OpCodes.Callvirt, Arguments_AppendMI, null);
163     }
164
165     private void EmitOut(ILGenerator generator, Type parType, int parN)
166     {
167       Type outParType = Arguments.MatchType(parType);
168       //generator.EmitWriteLine("enumerator.MoveNext()");
169       generator.Emit(OpCodes.Ldloc_2);
170       generator.EmitCall(OpCodes.Callvirt, IEnumerator_MoveNextMI, null);
171
172       //generator.EmitWriteLine("return (" + parType + ") ((DBusType.IDBusType) enumerator.Current).Get(typeof(" + parType + "))");
173       generator.Emit(OpCodes.Pop);
174       if (parN > 0) {
175         generator.Emit(OpCodes.Ldarg_S, parN + 1);
176       }
177       
178       generator.Emit(OpCodes.Ldloc_2);
179       generator.EmitCall(OpCodes.Callvirt, IEnumerator_CurrentMI, null);
180       generator.Emit(OpCodes.Castclass, typeof(DBusType.IDBusType));
181       generator.Emit(OpCodes.Ldtoken, parType);
182       generator.EmitCall(OpCodes.Call, Type_GetTypeFromHandleMI, null);
183       generator.EmitCall(OpCodes.Callvirt, IDBusType_GetMI, null);
184
185       // Call the DBusType EmitMarshalOut to make it emit itself
186       object[] pars = new object[] {generator, parType, parN == 0};
187       outParType.InvokeMember("EmitMarshalOut", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
188       
189       if (parN == 0) {
190         generator.Emit(OpCodes.Stloc_3);
191       }
192     }
193     
194     public void BuildConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
195     {
196       Type[] pars = {typeof(Service), typeof(string)};
197       ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName | 
198                                                                MethodAttributes.Public,
199                                                                CallingConventions.Standard, pars);
200
201       ILGenerator generator = constructor.GetILGenerator();
202       generator.Emit(OpCodes.Ldarg_0);
203       generator.Emit(OpCodes.Call, this.introspector.Constructor);
204       generator.Emit(OpCodes.Ldarg_1);
205       generator.Emit(OpCodes.Stsfld, serviceF);
206       generator.Emit(OpCodes.Ldarg_0);
207       generator.Emit(OpCodes.Ldarg_2);
208       generator.Emit(OpCodes.Stfld, pathF);
209
210       generator.Emit(OpCodes.Ret);
211     }
212     
213     public object GetProxy() 
214     {      
215       
216       Type proxyType = ProxyAssembly.GetType(ProxyName);
217       
218       if (proxyType == null) {
219         // Build the type
220         TypeBuilder typeB = ServiceModuleBuilder.DefineType(ProxyName, TypeAttributes.Public, this.type);
221         
222         FieldBuilder serviceF = typeB.DefineField("service", 
223                                                   typeof(Service), 
224                                                   FieldAttributes.Private | 
225                                                   FieldAttributes.Static);
226         FieldBuilder pathF = typeB.DefineField("pathName", 
227                                                typeof(string), 
228                                                FieldAttributes.Private);
229         
230         BuildConstructor(ref typeB, serviceF, pathF);
231         
232         // Build the methods
233         foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
234           InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
235           foreach (DictionaryEntry methodEntry in interfaceProxy.Methods) {
236             MethodInfo method = (MethodInfo) methodEntry.Value;
237             BuildMethod(method, interfaceProxy, ref typeB, serviceF, pathF);
238           }
239         }
240         
241         proxyType = typeB.CreateType();
242       
243         // Uncomment the following line to produce a DLL of the
244         // constructed assembly which can then be examined using
245         // monodis. Note that in order for this to work you should copy
246         // the client assembly as a dll file so that monodis can pick it
247         // up.
248         //ProxyAssembly.Save("proxy.dll");
249       }
250
251       Type [] parTypes = new Type[] {typeof(Service), typeof(string)};
252       object [] pars = new object[] {Service, pathName};
253       
254       ConstructorInfo constructor = proxyType.GetConstructor(parTypes);
255       object instance = constructor.Invoke(pars);
256       return instance;
257     }
258
259     private ModuleBuilder ServiceModuleBuilder
260     {
261       get {
262         if (Service.module == null) {
263           Service.module = ProxyAssembly.DefineDynamicModule(Service.Name, "proxy.dll", true);
264         }
265         
266         return Service.module;
267       }
268     }
269   
270   private Service Service
271     {
272       get {
273         return this.service;
274       }
275     }
276
277     private string ProxyName
278     {
279       get {
280         return this.introspector.ToString() + ".Proxy";
281       }
282     }
283
284     private AssemblyBuilder ProxyAssembly
285     {
286       get {
287         if (proxyAssembly == null){
288           AssemblyName assemblyName = new AssemblyName();
289           assemblyName.Name = "DBusProxy";
290           proxyAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, 
291                                                                    AssemblyBuilderAccess.RunAndSave);
292         }
293         
294         return proxyAssembly;
295       }
296     }
297   }
298 }
299