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;
11 internal class ProxyBuilder
13 private Service service= null;
14 private string pathName = null;
15 private Type type = null;
16 private Introspector introspector = null;
18 private static MethodInfo Service_NameMI = typeof(Service).GetMethod("get_Name",
20 private static MethodInfo Service_ConnectionMI = typeof(Service).GetMethod("get_Connection",
22 private static MethodInfo Service_AddSignalCalledMI = typeof(Service).GetMethod("add_SignalCalled",
23 new Type[] {typeof(Service.SignalCalledHandler)});
24 private static MethodInfo Signal_PathNameMI = typeof(Signal).GetMethod("get_PathName",
26 private static MethodInfo Message_ArgumentsMI = typeof(Message).GetMethod("get_Arguments",
28 private static MethodInfo Message_KeyMI = typeof(Message).GetMethod("get_Key",
30 private static MethodInfo Arguments_InitAppendingMI = typeof(Arguments).GetMethod("InitAppending",
32 private static MethodInfo Arguments_AppendMI = typeof(Arguments).GetMethod("Append",
33 new Type[] {typeof(DBusType.IDBusType)});
34 private static MethodInfo Message_SendWithReplyAndBlockMI = typeof(Message).GetMethod("SendWithReplyAndBlock",
36 private static MethodInfo Message_SendMI = typeof(Message).GetMethod("Send",
38 private static MethodInfo Arguments_GetEnumeratorMI = typeof(Arguments).GetMethod("GetEnumerator",
40 private static MethodInfo IEnumerator_MoveNextMI = typeof(System.Collections.IEnumerator).GetMethod("MoveNext",
42 private static MethodInfo IEnumerator_CurrentMI = typeof(System.Collections.IEnumerator).GetMethod("get_Current",
44 private static MethodInfo Type_GetTypeFromHandleMI = typeof(System.Type).GetMethod("GetTypeFromHandle",
45 new Type[] {typeof(System.RuntimeTypeHandle)});
46 private static MethodInfo IDBusType_GetMI = typeof(DBusType.IDBusType).GetMethod("Get",
47 new Type[] {typeof(System.Type)});
48 private static ConstructorInfo MethodCall_C = typeof(MethodCall).GetConstructor(new Type[] {typeof(Service),
52 private static ConstructorInfo Signal_C = typeof(Signal).GetConstructor(new Type[] {typeof(Service),
56 private static ConstructorInfo Service_SignalCalledHandlerC = typeof(Service.SignalCalledHandler).GetConstructor(new Type[] {typeof(object),
57 typeof(System.IntPtr)});
58 private static MethodInfo String_opEqualityMI = typeof(System.String).GetMethod("op_Equality",
59 new Type[] {typeof(string),
61 private static MethodInfo MulticastDelegate_opInequalityMI = typeof(System.MulticastDelegate).GetMethod("op_Inequality",
62 new Type[] {typeof(System.MulticastDelegate),
63 typeof(System.MulticastDelegate)});
66 public ProxyBuilder(Service service, Type type, string pathName)
68 this.service = service;
69 this.pathName = pathName;
71 this.introspector = Introspector.GetIntrospector(type);
74 private MethodInfo BuildSignalCalled(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
76 Type[] parTypes = {typeof(Signal)};
77 MethodBuilder methodBuilder = typeB.DefineMethod("Service_SignalCalled",
78 MethodAttributes.Private |
79 MethodAttributes.HideBySig,
83 ILGenerator generator = methodBuilder.GetILGenerator();
85 LocalBuilder enumeratorL = generator.DeclareLocal(typeof(System.Collections.IEnumerator));
86 enumeratorL.SetLocalSymInfo("enumerator");
88 Label wrongPath = generator.DefineLabel();
89 //generator.EmitWriteLine("if (signal.PathName == pathName) {");
90 generator.Emit(OpCodes.Ldarg_1);
91 generator.EmitCall(OpCodes.Callvirt, Signal_PathNameMI, null);
92 generator.Emit(OpCodes.Ldarg_0);
93 generator.Emit(OpCodes.Ldfld, pathF);
94 generator.EmitCall(OpCodes.Call, String_opEqualityMI, null);
95 generator.Emit(OpCodes.Brfalse, wrongPath);
99 foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
100 InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
101 foreach (DictionaryEntry signalEntry in interfaceProxy.Signals) {
102 EventInfo eventE = (EventInfo) signalEntry.Value;
103 // This is really cheeky since we need to grab the event as a private field.
104 FieldInfo eventF = this.type.GetField(eventE.Name,
105 BindingFlags.NonPublic|
106 BindingFlags.Instance);
108 MethodInfo eventHandler_InvokeMI = eventE.EventHandlerType.GetMethod("Invoke");
110 ParameterInfo[] pars = eventHandler_InvokeMI.GetParameters();
111 parTypes = new Type[pars.Length];
112 for (int parN = 0; parN < pars.Length; parN++) {
113 parTypes[parN] = pars[parN].ParameterType;
114 LocalBuilder parmL = generator.DeclareLocal(parTypes[parN]);
115 parmL.SetLocalSymInfo(pars[parN].Name);
118 Label skip = generator.DefineLabel();
119 //generator.EmitWriteLine(" if (SelectedIndexChanged != null) {");
120 generator.Emit(OpCodes.Ldarg_0);
121 generator.Emit(OpCodes.Ldfld, eventF);
122 generator.Emit(OpCodes.Ldnull);
123 generator.EmitCall(OpCodes.Call, MulticastDelegate_opInequalityMI, null);
124 generator.Emit(OpCodes.Brfalse, skip);
126 //generator.EmitWriteLine(" if (signal.Key == 'la i')");
127 generator.Emit(OpCodes.Ldarg_1);
128 generator.EmitCall(OpCodes.Callvirt, Message_KeyMI, null);
129 generator.Emit(OpCodes.Ldstr, eventE.Name + " " + InterfaceProxy.GetSignature(eventHandler_InvokeMI));
130 generator.EmitCall(OpCodes.Call, String_opEqualityMI, null);
131 generator.Emit(OpCodes.Brfalse, skip);
133 //generator.EmitWriteLine("IEnumerator enumerator = signal.Arguments.GetEnumerator()");
134 generator.Emit(OpCodes.Ldarg_1);
135 generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
136 generator.EmitCall(OpCodes.Callvirt, Arguments_GetEnumeratorMI, null);
137 generator.Emit(OpCodes.Stloc_0);
139 for (int parN = 0; parN < pars.Length; parN++) {
140 ParameterInfo par = pars[parN];
142 EmitSignalIn(generator, par.ParameterType, parN + localOffset, serviceF);
146 //generator.EmitWriteLine(" SelectedIndexChanged(selectedIndex)");
147 generator.Emit(OpCodes.Ldarg_0);
148 generator.Emit(OpCodes.Ldfld, eventF);
149 for (int parN = 0; parN < pars.Length; parN++) {
150 generator.Emit(OpCodes.Ldloc_S, parN + localOffset);
153 generator.EmitCall(OpCodes.Callvirt, eventHandler_InvokeMI, null);
155 generator.MarkLabel(skip);
156 //generator.EmitWriteLine(" }");
158 localOffset += pars.Length;
162 generator.MarkLabel(wrongPath);
163 //generator.EmitWriteLine("}");
165 //generator.EmitWriteLine("return");
166 generator.Emit(OpCodes.Ret);
168 return methodBuilder;
171 private void BuildSignalHandler(EventInfo eventE,
172 InterfaceProxy interfaceProxy,
173 ref TypeBuilder typeB,
177 MethodInfo eventHandler_InvokeMI = eventE.EventHandlerType.GetMethod("Invoke");
178 ParameterInfo[] pars = eventHandler_InvokeMI.GetParameters();
179 Type[] parTypes = new Type[pars.Length];
180 for (int parN = 0; parN < pars.Length; parN++) {
181 parTypes[parN] = pars[parN].ParameterType;
185 MethodBuilder methodBuilder = typeB.DefineMethod("Proxy_" + eventE.Name,
186 MethodAttributes.Public |
187 MethodAttributes.HideBySig |
188 MethodAttributes.Virtual,
191 ILGenerator generator = methodBuilder.GetILGenerator();
193 for (int parN = 0; parN < pars.Length; parN++) {
194 methodBuilder.DefineParameter(parN + 1, pars[parN].Attributes, pars[parN].Name);
197 // Generate the locals
198 LocalBuilder methodCallL = generator.DeclareLocal(typeof(MethodCall));
199 methodCallL.SetLocalSymInfo("signal");
200 LocalBuilder replyL = generator.DeclareLocal(typeof(MethodReturn));
202 //generator.EmitWriteLine("Signal signal = new Signal(...)");
203 generator.Emit(OpCodes.Ldsfld, serviceF);
204 generator.Emit(OpCodes.Ldarg_0);
205 generator.Emit(OpCodes.Ldfld, pathF);
206 generator.Emit(OpCodes.Ldstr, interfaceProxy.InterfaceName);
207 generator.Emit(OpCodes.Ldstr, eventE.Name);
208 generator.Emit(OpCodes.Newobj, Signal_C);
209 generator.Emit(OpCodes.Stloc_0);
211 //generator.EmitWriteLine("signal.Arguments.InitAppending()");
212 generator.Emit(OpCodes.Ldloc_0);
213 generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
214 generator.EmitCall(OpCodes.Callvirt, Arguments_InitAppendingMI, null);
216 for (int parN = 0; parN < pars.Length; parN++) {
217 ParameterInfo par = pars[parN];
219 EmitIn(generator, par.ParameterType, parN, serviceF);
223 //generator.EmitWriteLine("signal.Send()");
224 generator.Emit(OpCodes.Ldloc_0);
225 generator.EmitCall(OpCodes.Callvirt, Message_SendMI, null);
227 //generator.EmitWriteLine("return");
228 generator.Emit(OpCodes.Ret);
231 private void BuildMethod(MethodInfo method,
232 InterfaceProxy interfaceProxy,
233 ref TypeBuilder typeB,
237 ParameterInfo[] pars = method.GetParameters();
238 Type[] parTypes = new Type[pars.Length];
239 for (int parN = 0; parN < pars.Length; parN++) {
240 parTypes[parN] = pars[parN].ParameterType;
244 MethodBuilder methodBuilder = typeB.DefineMethod(method.Name,
245 MethodAttributes.Public |
246 MethodAttributes.HideBySig |
247 MethodAttributes.Virtual,
250 ILGenerator generator = methodBuilder.GetILGenerator();
252 for (int parN = 0; parN < pars.Length; parN++) {
253 methodBuilder.DefineParameter(parN + 1, pars[parN].Attributes, pars[parN].Name);
256 // Generate the locals
257 LocalBuilder methodCallL = generator.DeclareLocal(typeof(MethodCall));
258 methodCallL.SetLocalSymInfo("methodCall");
259 LocalBuilder replyL = generator.DeclareLocal(typeof(MethodReturn));
260 replyL.SetLocalSymInfo("reply");
261 LocalBuilder enumeratorL = generator.DeclareLocal(typeof(System.Collections.IEnumerator));
262 enumeratorL.SetLocalSymInfo("enumerator");
264 if (method.ReturnType != typeof(void)) {
265 LocalBuilder retvalL = generator.DeclareLocal(method.ReturnType);
266 retvalL.SetLocalSymInfo("retval");
269 //generator.EmitWriteLine("MethodCall methodCall = new MethodCall(...)");
270 generator.Emit(OpCodes.Ldsfld, serviceF);
271 generator.Emit(OpCodes.Ldarg_0);
272 generator.Emit(OpCodes.Ldfld, pathF);
273 generator.Emit(OpCodes.Ldstr, interfaceProxy.InterfaceName);
274 generator.Emit(OpCodes.Ldstr, method.Name);
275 generator.Emit(OpCodes.Newobj, MethodCall_C);
276 generator.Emit(OpCodes.Stloc_0);
278 //generator.EmitWriteLine("methodCall.Arguments.InitAppending()");
279 generator.Emit(OpCodes.Ldloc_0);
280 generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
281 generator.EmitCall(OpCodes.Callvirt, Arguments_InitAppendingMI, null);
283 for (int parN = 0; parN < pars.Length; parN++) {
284 ParameterInfo par = pars[parN];
286 EmitIn(generator, par.ParameterType, parN, serviceF);
290 //generator.EmitWriteLine("MethodReturn reply = methodCall.SendWithReplyAndBlock()");
291 generator.Emit(OpCodes.Ldloc_0);
292 generator.EmitCall(OpCodes.Callvirt, Message_SendWithReplyAndBlockMI, null);
293 generator.Emit(OpCodes.Stloc_1);
295 //generator.EmitWriteLine("IEnumerator enumeartor = reply.Arguments.GetEnumerator()");
296 generator.Emit(OpCodes.Ldloc_1);
297 generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
298 generator.EmitCall(OpCodes.Callvirt, Arguments_GetEnumeratorMI, null);
299 generator.Emit(OpCodes.Stloc_2);
301 // handle the return value
302 if (method.ReturnType != typeof(void)) {
303 EmitOut(generator, method.ReturnType, 0);
306 for (int parN = 0; parN < pars.Length; parN++) {
307 ParameterInfo par = pars[parN];
308 if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) {
309 EmitOut(generator, par.ParameterType, parN);
313 if (method.ReturnType != typeof(void)) {
314 generator.Emit(OpCodes.Ldloc_3);
317 generator.Emit(OpCodes.Ret);
319 // Generate the method
320 typeB.DefineMethodOverride(methodBuilder, method);
323 private void EmitSignalIn(ILGenerator generator, Type parType, int parN, FieldInfo serviceF)
325 //generator.EmitWriteLine("enumerator.MoveNext()");
326 generator.Emit(OpCodes.Ldloc_0);
327 generator.EmitCall(OpCodes.Callvirt, IEnumerator_MoveNextMI, null);
329 Type outParType = Arguments.MatchType(parType);
330 //generator.EmitWriteLine("int selectedIndex = (int) ((DBusType.IDBusType) enumerator.Current).Get(typeof(int))");
331 generator.Emit(OpCodes.Pop);
332 generator.Emit(OpCodes.Ldloc_0);
333 generator.EmitCall(OpCodes.Callvirt, IEnumerator_CurrentMI, null);
334 generator.Emit(OpCodes.Castclass, typeof(DBusType.IDBusType));
335 generator.Emit(OpCodes.Ldtoken, parType);
336 generator.EmitCall(OpCodes.Call, Type_GetTypeFromHandleMI, null);
337 generator.EmitCall(OpCodes.Callvirt, IDBusType_GetMI, null);
338 // Call the DBusType EmitMarshalOut to make it emit itself
339 object[] pars = new object[] {generator, parType, true};
340 outParType.InvokeMember("EmitMarshalOut", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
341 generator.Emit(OpCodes.Stloc_S, parN);
345 private void EmitIn(ILGenerator generator, Type parType, int parN, FieldInfo serviceF)
347 Type inParType = Arguments.MatchType(parType);
348 //generator.EmitWriteLine("methodCall.Arguments.Append(...)");
349 generator.Emit(OpCodes.Ldloc_0);
350 generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
351 generator.Emit(OpCodes.Ldarg_S, parN + 1);
353 // Call the DBusType EmitMarshalIn to make it emit itself
354 object[] pars = new object[] {generator, parType};
355 inParType.InvokeMember("EmitMarshalIn", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
357 generator.Emit(OpCodes.Ldsfld, serviceF);
358 generator.Emit(OpCodes.Newobj, Arguments.GetDBusTypeConstructor(inParType, parType));
359 generator.EmitCall(OpCodes.Callvirt, Arguments_AppendMI, null);
362 private void EmitOut(ILGenerator generator, Type parType, int parN)
364 Type outParType = Arguments.MatchType(parType);
365 //generator.EmitWriteLine("enumerator.MoveNext()");
366 generator.Emit(OpCodes.Ldloc_2);
367 generator.EmitCall(OpCodes.Callvirt, IEnumerator_MoveNextMI, null);
369 //generator.EmitWriteLine("return (" + parType + ") ((DBusType.IDBusType) enumerator.Current).Get(typeof(" + parType + "))");
370 generator.Emit(OpCodes.Pop);
372 generator.Emit(OpCodes.Ldarg_S, parN + 1);
375 generator.Emit(OpCodes.Ldloc_2);
376 generator.EmitCall(OpCodes.Callvirt, IEnumerator_CurrentMI, null);
377 generator.Emit(OpCodes.Castclass, typeof(DBusType.IDBusType));
378 generator.Emit(OpCodes.Ldtoken, parType);
379 generator.EmitCall(OpCodes.Call, Type_GetTypeFromHandleMI, null);
380 generator.EmitCall(OpCodes.Callvirt, IDBusType_GetMI, null);
382 // Call the DBusType EmitMarshalOut to make it emit itself
383 object[] pars = new object[] {generator, parType, parN == 0};
384 outParType.InvokeMember("EmitMarshalOut", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
387 generator.Emit(OpCodes.Stloc_3);
391 public void BuildConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF, MethodInfo signalCalledMI)
393 Type[] pars = {typeof(Service), typeof(string)};
394 ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName |
395 MethodAttributes.Public,
396 CallingConventions.Standard, pars);
398 ILGenerator generator = constructor.GetILGenerator();
399 generator.Emit(OpCodes.Ldarg_0);
400 generator.Emit(OpCodes.Call, this.introspector.Constructor);
401 //generator.EmitWriteLine("service = myService");
402 generator.Emit(OpCodes.Ldarg_1);
403 generator.Emit(OpCodes.Stsfld, serviceF);
404 //generator.EmitWriteLine("this.pathName = pathName");
405 generator.Emit(OpCodes.Ldarg_0);
406 generator.Emit(OpCodes.Ldarg_2);
407 generator.Emit(OpCodes.Stfld, pathF);
409 //generator.EmitWriteLine("myService.SignalCalled += new Service.SignalCalledHandler(Service_SignalCalled)");
410 generator.Emit(OpCodes.Ldarg_1);
411 generator.Emit(OpCodes.Ldarg_0);
412 generator.Emit(OpCodes.Ldftn, signalCalledMI);
413 generator.Emit(OpCodes.Newobj, Service_SignalCalledHandlerC);
414 generator.EmitCall(OpCodes.Callvirt, Service_AddSignalCalledMI, null);
416 //generator.EmitWriteLine("return");
417 generator.Emit(OpCodes.Ret);
420 public void BuildSignalConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
422 Type[] pars = {typeof(Service), typeof(string)};
423 ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName |
424 MethodAttributes.Public,
425 CallingConventions.Standard, pars);
427 ILGenerator generator = constructor.GetILGenerator();
428 generator.Emit(OpCodes.Ldarg_0);
429 generator.Emit(OpCodes.Call, this.introspector.Constructor);
430 //generator.EmitWriteLine("service = myService");
431 generator.Emit(OpCodes.Ldarg_1);
432 generator.Emit(OpCodes.Stsfld, serviceF);
433 //generator.EmitWriteLine("this.pathName = pathName");
434 generator.Emit(OpCodes.Ldarg_0);
435 generator.Emit(OpCodes.Ldarg_2);
436 generator.Emit(OpCodes.Stfld, pathF);
438 //generator.EmitWriteLine("return");
439 generator.Emit(OpCodes.Ret);
442 public object GetSignalProxy()
444 Type proxyType = Service.ProxyAssembly.GetType(ObjectName + ".SignalProxy");
446 if (proxyType == null) {
448 TypeBuilder typeB = Service.Module.DefineType(ObjectName + ".SignalProxy",
449 TypeAttributes.Public,
452 FieldBuilder serviceF = typeB.DefineField("service",
454 FieldAttributes.Private |
455 FieldAttributes.Static);
456 FieldBuilder pathF = typeB.DefineField("pathName",
458 FieldAttributes.Private);
460 BuildSignalConstructor(ref typeB, serviceF, pathF);
462 // Build the signal handlers
463 foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
464 InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
465 foreach (DictionaryEntry signalEntry in interfaceProxy.Signals) {
466 EventInfo eventE = (EventInfo) signalEntry.Value;
467 BuildSignalHandler(eventE, interfaceProxy, ref typeB, serviceF, pathF);
471 proxyType = typeB.CreateType();
473 // Uncomment the following line to produce a DLL of the
474 // constructed assembly which can then be examined using
475 // monodis. Note that in order for this to work you should copy
476 // the client assembly as a dll file so that monodis can pick it
478 //Service.ProxyAssembly.Save("proxy.dll");
481 Type [] parTypes = new Type[] {typeof(Service), typeof(string)};
482 object [] pars = new object[] {Service, pathName};
484 ConstructorInfo constructor = proxyType.GetConstructor(parTypes);
485 object instance = constructor.Invoke(pars);
490 public object GetProxy()
492 Type proxyType = Service.ProxyAssembly.GetType(ObjectName + ".Proxy");
494 if (proxyType == null) {
496 TypeBuilder typeB = Service.Module.DefineType(ObjectName + ".Proxy", TypeAttributes.Public, this.type);
498 FieldBuilder serviceF = typeB.DefineField("service",
500 FieldAttributes.Private |
501 FieldAttributes.Static);
502 FieldBuilder pathF = typeB.DefineField("pathName",
504 FieldAttributes.Private);
506 MethodInfo signalCalledMI = BuildSignalCalled(ref typeB, serviceF, pathF);
507 BuildConstructor(ref typeB, serviceF, pathF, signalCalledMI);
510 foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
511 InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
512 foreach (DictionaryEntry methodEntry in interfaceProxy.Methods) {
513 MethodInfo method = (MethodInfo) methodEntry.Value;
514 BuildMethod(method, interfaceProxy, ref typeB, serviceF, pathF);
518 proxyType = typeB.CreateType();
520 // Uncomment the following line to produce a DLL of the
521 // constructed assembly which can then be examined using
522 // monodis. Note that in order for this to work you should copy
523 // the client assembly as a dll file so that monodis can pick it
525 //Service.ProxyAssembly.Save("proxy.dll");
528 Type [] parTypes = new Type[] {typeof(Service), typeof(string)};
529 object [] pars = new object[] {Service, pathName};
531 ConstructorInfo constructor = proxyType.GetConstructor(parTypes);
532 object instance = constructor.Invoke(pars);
536 private Service Service
543 private string ObjectName
546 return this.introspector.ToString();