1
// Copyright 2006 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
6
using System.Reflection;
7
using System.Reflection.Emit;
11
//marked internal because this isn't ready for public use yet
12
//it probably needs to be made into a base and import/export subclasses
13
internal class BusObject
17
ObjectPath object_path;
19
//protected BusObject ()
24
public BusObject (Connection conn, string bus_name, ObjectPath object_path)
27
this.bus_name = bus_name;
28
this.object_path = object_path;
31
public Connection Connection
45
public ObjectPath Path
53
public object InvokeMethod (MethodInfo methodInfo, params object[] inArgs)
55
//TODO: this ignores outArgs, doesn't wrap the exception etc.
61
Invoke (methodInfo, methodInfo.Name, inArgs, out outArgs, out retVal, out exception);
63
if (exception != null)
69
//this method is kept simple while IL generation support is worked on
70
public object SendMethodCall (MethodInfo methodInfo, string @interface, string member, object[] inArgs)
72
//TODO: don't ignore retVal, exception etc.
77
Invoke (methodInfo, methodInfo.Name, inArgs, out outArgs, out retVal, out exception);
79
if (exception != null)
85
public void Invoke (MethodBase methodBase, string methodName, object[] inArgs, out object[] outArgs, out object retVal, out Exception exception)
87
outArgs = new object[0];
91
MethodInfo mi = methodBase as MethodInfo;
93
if (mi != null && mi.IsSpecialName && (methodName.StartsWith ("add_") || methodName.StartsWith ("remove_"))) {
94
string[] parts = methodName.Split (new char[]{'_'}, 2);
95
string ename = parts[1];
96
Delegate dlg = (Delegate)inArgs[0];
97
string matchRule = MessageFilter.CreateMatchRule (MessageType.Signal, object_path, Mapper.GetInterfaceName (mi), ename);
99
if (parts[0] == "add") {
100
if (conn.Handlers.ContainsKey (matchRule))
101
conn.Handlers[matchRule] = Delegate.Combine (conn.Handlers[matchRule], dlg);
103
conn.Handlers[matchRule] = dlg;
104
conn.AddMatch (matchRule);
106
} else if (parts[0] == "remove") {
107
conn.Handlers[matchRule] = Delegate.Remove (conn.Handlers[matchRule], dlg);
108
if (conn.Handlers[matchRule] == null) {
109
conn.RemoveMatch (matchRule);
110
conn.Handlers.Remove (matchRule);
116
Type[] inTypes = Mapper.GetTypes (ArgDirection.In, mi.GetParameters ());
117
Signature inSig = Signature.GetSig (inTypes);
119
MethodCall method_call;
122
//build the outbound method call message
124
//this bit is error-prone (no null checking) and will need rewriting when DProxy is replaced
127
iface = Mapper.GetInterfaceName (mi);
129
//map property accessors
130
//TODO: this needs to be done properly, not with simple String.Replace
131
//note that IsSpecialName is also for event accessors, but we already handled those and returned
132
if (mi != null && mi.IsSpecialName) {
133
methodName = methodName.Replace ("get_", "Get");
134
methodName = methodName.Replace ("set_", "Set");
137
method_call = new MethodCall (object_path, iface, methodName, bus_name, inSig);
139
callMsg = method_call.message;
141
if (inArgs != null && inArgs.Length != 0) {
142
MessageWriter writer = new MessageWriter ();
144
for (int i = 0 ; i != inTypes.Length ; i++)
145
writer.Write (inTypes[i], inArgs[i]);
147
callMsg.Body = writer.ToArray ();
151
//TODO: complete out parameter support
152
Type[] outParmTypes = Mapper.GetTypes (ArgDirection.Out, mi.GetParameters ());
153
Signature outParmSig = Signature.GetSig (outParmTypes);
155
if (outParmSig != Signature.Empty)
156
throw new Exception ("Out parameters not yet supported: out_signature='" + outParmSig.Value + "'");
158
Type[] outTypes = new Type[1];
159
outTypes[0] = mi.ReturnType;
161
//we default to always requiring replies for now, even though unnecessary
162
//this is to make sure errors are handled synchronously
163
//TODO: don't hard code this
164
bool needsReply = true;
166
//if (mi.ReturnType == typeof (void))
167
// needsReply = false;
169
callMsg.ReplyExpected = needsReply;
170
callMsg.Signature = inSig;
177
#if PROTO_REPLY_SIGNATURE
179
Signature outSig = Signature.GetSig (outTypes);
180
callMsg.Header.Fields[FieldCode.ReplySignature] = outSig;
184
Message retMsg = conn.SendWithReplyAndBlock (callMsg);
186
//handle the reply message
187
switch (retMsg.Header.MessageType) {
188
case MessageType.MethodReturn:
189
object[] retVals = MessageHelper.GetDynamicValues (retMsg, outTypes);
190
if (retVals.Length != 0)
191
retVal = retVals[retVals.Length - 1];
193
case MessageType.Error:
194
//TODO: typed exceptions
195
Error error = new Error (retMsg);
197
if (retMsg.Signature.Value.StartsWith ("s")) {
198
MessageReader reader = new MessageReader (retMsg);
199
reader.GetValue (out errMsg);
201
exception = new Exception (error.ErrorName + ": " + errMsg);
204
throw new Exception ("Got unexpected message of type " + retMsg.Header.MessageType + " while waiting for a MethodReturn or Error");
210
static AssemblyBuilder asmB;
211
static ModuleBuilder modB;
213
static void InitHack ()
218
asmB = AppDomain.CurrentDomain.DefineDynamicAssembly (new AssemblyName ("ProxyAssembly"), AssemblyBuilderAccess.Run);
219
modB = asmB.DefineDynamicModule ("ProxyModule");
222
static System.Collections.Generic.Dictionary<Type,Type> map = new System.Collections.Generic.Dictionary<Type,Type> ();
224
public static Type DefineType (Type declType)
226
if (map.ContainsKey (declType))
227
return map[declType];
231
TypeBuilder typeB = modB.DefineType (declType.Name, TypeAttributes.Class | TypeAttributes.Public, typeof (BusObject));
233
Implement (typeB, declType);
235
foreach (Type iface in declType.GetInterfaces ()) {
236
Implement (typeB, iface);
237
//typeB.DefineMethodOverride (body, declMethod);
240
Type retT = typeB.CreateType ();
242
map[declType] = retT;
243
//return typeB.CreateType ();
247
public static void Implement (TypeBuilder typeB, Type iface)
249
typeB.AddInterfaceImplementation (iface);
251
foreach (MethodInfo declMethod in iface.GetMethods ()) {
253
MethodBuilder method_builder = typeB.DefineMethod (declMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, declMethod.ReturnType, Mapper.GetTypes (ArgDirection.In, declMethod.GetParameters ()));
254
ILGenerator ilg = method_builder.GetILGenerator ();
256
//Mapper.GetTypes (ArgDirection.In, declMethod.GetParameters ())
258
ParameterInfo[] delegateParms = declMethod.GetParameters ();
259
Type[] hookupParms = new Type[delegateParms.Length+1];
260
hookupParms[0] = typeof (BusObject);
261
for (int i = 0; i < delegateParms.Length ; i++)
262
hookupParms[i+1] = delegateParms[i].ParameterType;
264
GenHookupMethod (ilg, declMethod, sendMethodCallMethod, Mapper.GetInterfaceName (iface), declMethod.Name, hookupParms);
268
public static object GetObject (Connection conn, string bus_name, ObjectPath object_path, Type declType)
270
Type proxyType = DefineType (declType);
272
BusObject inst = (BusObject)Activator.CreateInstance (proxyType);
274
inst.bus_name = bus_name;
275
inst.object_path = object_path;
280
static MethodInfo sendMethodCallMethod = typeof (BusObject).GetMethod ("SendMethodCall");
281
static MethodInfo sendSignalMethod = typeof (BusObject).GetMethod ("SendSignal");
283
public Delegate GetHookupDelegate (EventInfo ei)
285
if (ei.EventHandlerType.IsAssignableFrom (typeof (System.EventHandler)))
286
Console.Error.WriteLine ("Warning: Cannot yet fully marshal EventHandler and its subclasses: " + ei.EventHandlerType);
288
MethodInfo declMethod = ei.EventHandlerType.GetMethod ("Invoke");
290
DynamicMethod hookupMethod = GetHookupMethod (declMethod, sendSignalMethod, Mapper.GetInterfaceName (ei), ei.Name);
292
Delegate d = hookupMethod.CreateDelegate (ei.EventHandlerType, this);
297
public static DynamicMethod GetHookupMethod (MethodInfo declMethod, MethodInfo invokeMethod, string @interface, string member)
299
ParameterInfo[] delegateParms = declMethod.GetParameters ();
300
Type[] hookupParms = new Type[delegateParms.Length+1];
301
hookupParms[0] = typeof (BusObject);
302
for (int i = 0; i < delegateParms.Length ; i++)
303
hookupParms[i+1] = delegateParms[i].ParameterType;
305
DynamicMethod hookupMethod = new DynamicMethod ("Handle" + member, declMethod.ReturnType, hookupParms, typeof (object));
307
ILGenerator ilg = hookupMethod.GetILGenerator ();
309
GenHookupMethod (ilg, declMethod, invokeMethod, @interface, member, hookupParms);
314
public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, MethodInfo invokeMethod, string @interface, string member, Type[] hookupParms)
316
Type retType = declMethod.ReturnType;
318
//the BusObject instance
319
ilg.Emit (OpCodes.Ldarg_0);
322
ilg.Emit (OpCodes.Ldtoken, declMethod);
323
ilg.Emit (OpCodes.Call, typeof (MethodBase).GetMethod ("GetMethodFromHandle"));
326
ilg.Emit (OpCodes.Ldstr, @interface);
329
ilg.Emit (OpCodes.Ldstr, member);
331
LocalBuilder local = ilg.DeclareLocal (typeof (object[]));
332
ilg.Emit (OpCodes.Ldc_I4, hookupParms.Length - 1);
333
ilg.Emit (OpCodes.Newarr, typeof (object));
334
ilg.Emit (OpCodes.Stloc, local);
336
//offset by one because arg0 is the instance of the delegate
337
for (int i = 1 ; i < hookupParms.Length ; i++)
339
Type t = hookupParms[i];
341
ilg.Emit (OpCodes.Ldloc, local);
342
//the instance parameter requires the -1 offset here
343
ilg.Emit (OpCodes.Ldc_I4, i-1);
344
ilg.Emit (OpCodes.Ldarg, i);
347
ilg.Emit (OpCodes.Box, t);
349
ilg.Emit (OpCodes.Stelem_Ref);
352
ilg.Emit (OpCodes.Ldloc, local);
353
ilg.Emit (OpCodes.Call, invokeMethod);
355
if (retType == typeof (void)) {
356
//we aren't expecting a return value, so throw away the (hopefully) null return
357
if (invokeMethod.ReturnType != typeof (void))
358
ilg.Emit (OpCodes.Pop);
360
if (retType.IsValueType)
361
ilg.Emit (OpCodes.Unbox_Any, retType);
363
ilg.Emit (OpCodes.Castclass, retType);
366
ilg.Emit (OpCodes.Ret);
369
public void SendSignal (MethodInfo mi, string @interface, string member, object[] outValues)
371
//TODO: make use of bus_name?
373
Type[] outTypes = Mapper.GetTypes (ArgDirection.In, mi.GetParameters ());
374
Signature outSig = Signature.GetSig (outTypes);
376
Signal signal = new Signal (object_path, @interface, member);
377
signal.message.Signature = outSig;
379
if (outValues != null && outValues.Length != 0) {
380
MessageWriter writer = new MessageWriter ();
382
for (int i = 0 ; i != outTypes.Length ; i++)
383
writer.Write (outTypes[i], outValues[i]);
385
signal.message.Body = writer.ToArray ();
388
conn.Send (signal.message);