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
.Collections
.Generic
;
7 using System
.Reflection
;
13 //TODO: move these Get*Name helpers somewhere more appropriate
14 public static string GetArgumentName (ParameterInfo pi
)
16 string argName
= pi
.Name
;
18 if (pi
.IsRetval
&& String
.IsNullOrEmpty (argName
))
21 return GetArgumentName ((ICustomAttributeProvider
)pi
, argName
);
24 public static string GetArgumentName (ICustomAttributeProvider attrProvider
, string defaultName
)
26 string argName
= defaultName
;
28 //TODO: no need for foreach
29 foreach (ArgumentAttribute aa
in attrProvider
.GetCustomAttributes (typeof (ArgumentAttribute
), true))
35 //TODO: these two methods are quite messy and need review
36 public static IEnumerable
<MemberInfo
> GetPublicMembers (Type type
)
38 //note that Type.GetInterfaces() returns all interfaces with flattened hierarchy
39 foreach (Type ifType
in type
.GetInterfaces ())
40 foreach (MemberInfo mi
in GetDeclaredPublicMembers (ifType
))
44 foreach (MemberInfo mi
in GetDeclaredPublicMembers (type
))
48 static IEnumerable
<MemberInfo
> GetDeclaredPublicMembers (Type type
)
51 foreach (MemberInfo mi
in type
.GetMembers (BindingFlags
.Public
| BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
))
55 //this method walks the interface tree in an undefined manner and returns the first match, or if no matches are found, null
56 //the logic needs review and cleanup
57 //TODO: unify member name mapping as is already done with interfaces and args
58 public static MethodInfo
GetMethod (Type type
, MethodCall method_call
)
60 foreach (MemberInfo member
in Mapper
.GetPublicMembers (type
)) {
61 //this could be made more efficient by using the given interface name earlier and avoiding walking through all public interfaces
62 if (method_call
.Interface
!= null)
63 if (GetInterfaceName (member
) != method_call
.Interface
)
66 MethodInfo meth
= null;
67 Type
[] inTypes
= null;
69 if (member
is PropertyInfo
) {
70 PropertyInfo prop
= member
as PropertyInfo
;
72 MethodInfo getter
= prop
.GetGetMethod (false);
73 MethodInfo setter
= prop
.GetSetMethod (false);
75 if (getter
!= null && "Get" + prop
.Name
== method_call
.Member
) {
77 inTypes
= Type
.EmptyTypes
;
78 } else if (setter
!= null && "Set" + prop
.Name
== method_call
.Member
) {
80 inTypes
= new Type
[] {prop.PropertyType}
;
83 meth
= member
as MethodInfo
;
88 if (meth
.Name
!= method_call
.Member
)
91 inTypes
= Mapper
.GetTypes (ArgDirection
.In
, meth
.GetParameters ());
94 if (meth
== null || inTypes
== null)
97 Signature inSig
= Signature
.GetSig (inTypes
);
99 if (inSig
!= method_call
.Signature
)
108 public static bool IsPublic (MemberInfo mi
)
110 return IsPublic (mi
.DeclaringType
);
113 public static bool IsPublic (Type type
)
115 //we need to have a proper look at what's really public at some point
116 //this will do for now
118 if (type
.IsDefined (typeof (InterfaceAttribute
), false))
121 if (type
.IsSubclassOf (typeof (MarshalByRefObject
)))
127 public static string GetInterfaceName (MemberInfo mi
)
129 return GetInterfaceName (mi
.DeclaringType
);
132 public static string GetInterfaceName (Type type
)
134 string interfaceName
= type
.FullName
;
136 //TODO: better fallbacks and namespace mangling when no InterfaceAttribute is available
138 //TODO: no need for foreach
139 foreach (InterfaceAttribute ia
in type
.GetCustomAttributes (typeof (InterfaceAttribute
), true))
140 interfaceName
= ia
.Name
;
142 return interfaceName
;
145 public static Type
[] GetTypes (ArgDirection dir
, ParameterInfo
[] parms
)
147 List
<Type
> types
= new List
<Type
> ();
149 //TODO: consider InOut/Ref
151 for (int i
= 0 ; i
!= parms
.Length
; i
++) {
153 case ArgDirection
.In
:
154 //docs say IsIn isn't reliable, and this is indeed true
157 types
.Add (parms
[i
].ParameterType
);
159 case ArgDirection
.Out
:
160 if (parms
[i
].IsOut
) {
161 //TODO: note that IsOut is optional to the compiler, we may want to use IsByRef instead
162 //eg: if (parms[i].ParameterType.IsByRef)
163 types
.Add (parms
[i
].ParameterType
.GetElementType ());
169 return types
.ToArray ();
172 public static bool IsDeprecated (ICustomAttributeProvider attrProvider
)
174 return attrProvider
.IsDefined (typeof (ObsoleteAttribute
), true);
177 static bool AreEqual (Type
[] a
, Type
[] b
)
179 if (a
.Length
!= b
.Length
)
182 for (int i
= 0 ; i
!= a
.Length
; i
++)
189 //workaround for Mono bug #81035 (memory leak)
190 static List
<Type
> genTypes
= new List
<Type
> ();
191 internal static Type
GetGenericType (Type defType
, Type
[] parms
)
193 foreach (Type genType
in genTypes
) {
194 if (genType
.GetGenericTypeDefinition () != defType
)
197 Type
[] genParms
= genType
.GetGenericArguments ();
199 if (!AreEqual (genParms
, parms
))
205 Type type
= defType
.MakeGenericType (parms
);
211 //TODO: this class is messy, move the methods somewhere more appropriate
212 static class MessageHelper
214 public static Message
CreateUnknownMethodError (MethodCall method_call
)
216 if (!method_call
.message
.ReplyExpected
)
219 string errMsg
= String
.Format ("Method \"{0}\" with signature \"{1}\" on interface \"{2}\" doesn't exist", method_call
.Member
, method_call
.Signature
.Value
, method_call
.Interface
);
221 Error error
= new Error ("org.freedesktop.DBus.Error.UnknownMethod", method_call
.message
.Header
.Serial
);
222 error
.message
.Signature
= new Signature (DType
.String
);
224 MessageWriter writer
= new MessageWriter (Connection
.NativeEndianness
);
225 writer
.Write (errMsg
);
226 error
.message
.Body
= writer
.ToArray ();
228 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
229 if (method_call
.Sender
!= null)
230 error
.message
.Header
.Fields
[FieldCode
.Destination
] = method_call
.Sender
;
232 return error
.message
;
235 public static void WriteDynamicValues (MessageWriter mw
, ParameterInfo
[] parms
, object[] vals
)
237 foreach (ParameterInfo parm
in parms
) {
241 Type actualType
= parm
.ParameterType
.GetElementType ();
242 mw
.Write (actualType
, vals
[parm
.Position
]);
246 public static object[] GetDynamicValues (Message msg
, ParameterInfo
[] parms
)
248 object[] vals
= new object[parms
.Length
];
250 if (msg
.Body
!= null) {
251 MessageReader reader
= new MessageReader (msg
);
252 foreach (ParameterInfo parm
in parms
) {
256 vals
[parm
.Position
] = reader
.ReadValue(parm
.ParameterType
);
263 public static object[] GetDynamicValues (Message msg
, Type
[] types
)
265 //TODO: this validation check should provide better information, eg. message dump or a stack trace, or at least the interface/member
266 if (Protocol
.Verbose
) {
267 Signature expected
= Signature
.GetSig (types
);
268 Signature actual
= msg
.Signature
;
269 if (actual
!= expected
)
270 Console
.Error
.WriteLine ("Warning: The signature of the message does not match that of the handler: " + "Expected '" + expected
+ "', got '" + actual
+ "'");
273 object[] vals
= new object[types
.Length
];
275 if (msg
.Body
!= null) {
276 MessageReader reader
= new MessageReader (msg
);
278 for (int i
= 0 ; i
!= types
.Length
; i
++)
279 vals
[i
] = reader
.ReadValue (types
[i
]);
285 public static Message
ConstructReply (MethodCall method_call
, params object[] vals
)
287 MethodReturn method_return
= new MethodReturn (method_call
.message
.Header
.Serial
);
288 Message replyMsg
= method_return
.message
;
290 Signature inSig
= Signature
.GetSig (vals
);
292 if (vals
!= null && vals
.Length
!= 0) {
293 MessageWriter writer
= new MessageWriter (Connection
.NativeEndianness
);
295 foreach (object arg
in vals
)
296 writer
.Write (arg
.GetType (), arg
);
298 replyMsg
.Body
= writer
.ToArray ();
301 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
302 if (method_call
.Sender
!= null)
303 replyMsg
.Header
.Fields
[FieldCode
.Destination
] = method_call
.Sender
;
305 replyMsg
.Signature
= inSig
;
307 //replyMsg.WriteHeader ();
312 public static Message
ConstructDynamicReply (MethodCall method_call
, MethodInfo mi
, object retVal
, object[] vals
)
314 Type retType
= mi
.ReturnType
;
316 MethodReturn method_return
= new MethodReturn (method_call
.message
.Header
.Serial
);
317 Message replyMsg
= method_return
.message
;
319 Signature outSig
= Signature
.GetSig (retType
);
320 outSig
+= Signature
.GetSig (Mapper
.GetTypes (ArgDirection
.Out
, mi
.GetParameters ()));
322 if (outSig
!= Signature
.Empty
) {
323 MessageWriter writer
= new MessageWriter (Connection
.NativeEndianness
);
325 //first write the return value, if any
326 if (retType
!= null && retType
!= typeof (void))
327 writer
.Write (retType
, retVal
);
329 //then write the out args
330 WriteDynamicValues (writer
, mi
.GetParameters (), vals
);
332 replyMsg
.Body
= writer
.ToArray ();
335 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
336 if (method_call
.Sender
!= null)
337 replyMsg
.Header
.Fields
[FieldCode
.Destination
] = method_call
.Sender
;
339 replyMsg
.Signature
= outSig
;
345 [AttributeUsage (AttributeTargets
.Interface
| AttributeTargets
.Class
, AllowMultiple
=false, Inherited
=true)]
346 public class InterfaceAttribute
: Attribute
350 public InterfaceAttribute (string name
)
356 [AttributeUsage (AttributeTargets
.Parameter
| AttributeTargets
.ReturnValue
, AllowMultiple
=false, Inherited
=true)]
357 public class ArgumentAttribute
: Attribute
361 public ArgumentAttribute (string name
)