From aa7852f3d7b57cf31565210be577f58dc007f3a9 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Thu, 19 Jun 2014 18:44:54 +0100 Subject: [PATCH 1/9] implementor: Correct F# Event Identification The MethodInfo.IsSpecialName property doesn't appear to be set true for event add/remove methods produced with the F# compiler, even with the [] annotation applied to the event. This appears partly due to F#'s generalisation of the signature for event handler delegate types, but also because we attempt to treat all methods equally when implementing them. This patch provides a quick hack to solve the problem with minimal change to the TypeImplementor class. --- src/TypeImplementer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index 5e6839f..2e2edda 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -3,6 +3,7 @@ // See COPYING for details using System; +using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; @@ -88,6 +89,10 @@ static void Implement (TypeBuilder typeB, Type iface, string interfaceName) Dictionary builders = new Dictionary (); + HashSet props_and_events = new HashSet (iface.GetMethods () + .Where (x => iface.GetProperties ().Any (y => x == y.GetGetMethod (true) || x == y.GetSetMethod (true)) || + iface.GetEvents ().Any (y => x == y.GetAddMethod (true) || x == y.GetRemoveMethod (true)))); + foreach (MethodInfo declMethod in iface.GetMethods ()) { ParameterInfo[] parms = declMethod.GetParameters (); @@ -108,7 +113,8 @@ static void Implement (TypeBuilder typeB, Type iface, string interfaceName) ILGenerator ilg = method_builder.GetILGenerator (); GenHookupMethod (ilg, declMethod, sendMethodCallMethod, interfaceName, declMethod.Name); - if (declMethod.IsSpecialName) + // Is Event Or Property + if (props_and_events.Contains (declMethod)) builders[declMethod.Name] = method_builder; } @@ -258,7 +264,7 @@ public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, Meth ilg.Emit (OpCodes.Ldstr, @interface); //special case event add/remove methods - if (declMethod.IsSpecialName && (declMethod.Name.StartsWith ("add_") || declMethod.Name.StartsWith ("remove_"))) { + if (declMethod.Name.StartsWith ("add_") || declMethod.Name.StartsWith ("remove_")) { string[] parts = declMethod.Name.Split (new char[]{'_'}, 2); string ename = parts[1]; From ae720e179fac74fb0e2af98e2e6e3add6f9da070 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Sun, 22 Jun 2014 22:36:31 +0100 Subject: [PATCH 2/9] implementer: Robust event/property identifcation Since the previous commit was a hack, and slightly questionable, this one builds upon it by adding a class with two simple extension methods for MethodBase objects; IsEvent and IsProperty. They are used to ensure the correct branches are followed when generating hookup methods for proxy classes since the IsSpecialName property is unreliable on interfaces originating in F#. --- src/TypeImplementer.cs | 53 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index 2e2edda..73d4f82 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -89,10 +89,6 @@ static void Implement (TypeBuilder typeB, Type iface, string interfaceName) Dictionary builders = new Dictionary (); - HashSet props_and_events = new HashSet (iface.GetMethods () - .Where (x => iface.GetProperties ().Any (y => x == y.GetGetMethod (true) || x == y.GetSetMethod (true)) || - iface.GetEvents ().Any (y => x == y.GetAddMethod (true) || x == y.GetRemoveMethod (true)))); - foreach (MethodInfo declMethod in iface.GetMethods ()) { ParameterInfo[] parms = declMethod.GetParameters (); @@ -114,7 +110,7 @@ static void Implement (TypeBuilder typeB, Type iface, string interfaceName) GenHookupMethod (ilg, declMethod, sendMethodCallMethod, interfaceName, declMethod.Name); // Is Event Or Property - if (props_and_events.Contains (declMethod)) + if (declMethod.IsEvent () || declMethod.IsProperty ()) builders[declMethod.Name] = method_builder; } @@ -264,7 +260,7 @@ public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, Meth ilg.Emit (OpCodes.Ldstr, @interface); //special case event add/remove methods - if (declMethod.Name.StartsWith ("add_") || declMethod.Name.StartsWith ("remove_")) { + if (declMethod.IsEvent () && (declMethod.Name.StartsWith ("add_") || declMethod.Name.StartsWith ("remove_"))) { string[] parts = declMethod.Name.Split (new char[]{'_'}, 2); string ename = parts[1]; @@ -571,6 +567,51 @@ static void GenTypeOf (ILGenerator ilg, Type t) } } + internal static class MethodBaseExtensions { + + static IDictionary> events = new Dictionary>(); + static IDictionary> properties = new Dictionary>(); + + private static void InitialiseType (Type type) + { + lock (typeof(MethodBaseExtensions)) { + if (events.ContainsKey (type) && properties.ContainsKey (type)) + return; + + events [type] = new HashSet(); + properties [type] = new HashSet(); + + type.GetEvents ().Aggregate (events [type], (set, evt) => { + set.Add (evt.GetAddMethod ()); + set.Add (evt.GetRemoveMethod ()); + return set; + }); + type.GetProperties ().Aggregate (properties [type], (set, prop) => { + set.Add (prop.GetGetMethod ()); + set.Add (prop.GetSetMethod ()); + return set; + }); + + events [type].Remove (null); + properties [type].Remove (null); + } + } + + public static bool IsEvent (this MethodBase method) + { + InitialiseType (method.DeclaringType); + HashSet methods = events [method.DeclaringType]; + return methods.Contains (method); + } + + public static bool IsProperty (this MethodBase method) + { + InitialiseType (method.DeclaringType); + HashSet methods = properties [method.DeclaringType]; + return methods.Contains (method); + } + } + internal delegate void TypeWriter (MessageWriter writer, T value); internal delegate void MethodCaller (object instance, MessageReader rdr, Message msg, MessageWriter ret); From 0cc989c0ec5e457c80e4541110e85a875fbeb111 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Fri, 30 May 2014 17:18:47 +0100 Subject: [PATCH 3/9] proxy: Enable DBus property extraction via CLR properties Properties were previously involved in some name mangling to separate them from normal methods, events were dealt with in a similar way. As result, although signals worked correctly, properties did not. This patch flips the implementation process on its head, first building a set of methods for evaluation and removing pairs as properties or signals (events) are implemented before passing the remainder to the general function. Property Get/Set is handled by two additional functions in the BusObject class, the IL is generated specifically for them. Event listener addition and removal IL generation is refactored into its own function in the same way. --- src/BusObject.cs | 23 ++++++ src/TypeImplementer.cs | 182 +++++++++++++++++++++++++++-------------- 2 files changed, 145 insertions(+), 60 deletions(-) diff --git a/src/BusObject.cs b/src/BusObject.cs index f8509b5..d7827d9 100644 --- a/src/BusObject.cs +++ b/src/BusObject.cs @@ -162,6 +162,29 @@ public MessageReader SendMethodCall (string iface, string member, string inSigSt return retVal; } + public object SendPropertyGet (string iface, string property) + { + Exception exception; + MessageWriter writer = new MessageWriter (); + writer.Write (iface); + writer.Write (property); + + MessageReader reader = SendMethodCall ("org.freedesktop.DBus.Properties", "Get", "ss", writer, typeof(object), out exception); + + return reader.ReadVariant (); + } + + public void SendPropertySet (string iface, string property, object value) + { + Exception exception; + MessageWriter writer = new MessageWriter (); + writer.Write (iface); + writer.Write (property); + writer.Write (typeof(object), value); + + SendMethodCall ("org.freedesktop.DBus.Properties", "Set", "ssv", writer, typeof(void), out exception); + } + public void Invoke (MethodBase methodBase, string methodName, object[] inArgs, out object[] outArgs, out object retVal, out Exception exception) { outArgs = new object[0]; diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index 73d4f82..5b3c8f7 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -35,6 +35,8 @@ class TypeImplementer static Dictionary writeMethods = new Dictionary (); static Dictionary typeWriters = new Dictionary (); + static MethodInfo sendPropertyGetMethod = typeof (BusObject).GetMethod ("SendPropertyGet"); + static MethodInfo sendPropertySetMethod = typeof (BusObject).GetMethod ("SendPropertySet"); static MethodInfo sendMethodCallMethod = typeof (BusObject).GetMethod ("SendMethodCall"); static MethodInfo sendSignalMethod = typeof (BusObject).GetMethod ("SendSignal"); static MethodInfo toggleSignalMethod = typeof (BusObject).GetMethod ("ToggleSignal"); @@ -87,53 +89,44 @@ static void Implement (TypeBuilder typeB, Type iface, string interfaceName) { typeB.AddInterfaceImplementation (iface); - Dictionary builders = new Dictionary (); + HashSet evaluation_set = new HashSet (iface.GetMethods ()); - foreach (MethodInfo declMethod in iface.GetMethods ()) { - ParameterInfo[] parms = declMethod.GetParameters (); + foreach (PropertyInfo declProp in iface.GetProperties ()) { + GenHookupProperty (typeB, declProp, interfaceName, evaluation_set); + } - Type[] parmTypes = new Type[parms.Length]; - for (int i = 0 ; i < parms.Length ; i++) - parmTypes[i] = parms[i].ParameterType; + foreach (EventInfo declEvent in iface.GetEvents ()) { + GenHookupEvent (typeB, declEvent, interfaceName, evaluation_set); + } - MethodAttributes attrs = declMethod.Attributes ^ MethodAttributes.Abstract; - attrs ^= MethodAttributes.NewSlot; - attrs |= MethodAttributes.Final; - MethodBuilder method_builder = typeB.DefineMethod (declMethod.Name, attrs, declMethod.ReturnType, parmTypes); - typeB.DefineMethodOverride (method_builder, declMethod); + foreach (MethodInfo declMethod in evaluation_set) { + MethodBuilder builder = CreateMethodBuilder (typeB, declMethod); + ILGenerator ilg = builder.GetILGenerator (); + GenHookupMethod (ilg, declMethod, sendMethodCallMethod, Mapper.GetInterfaceName (iface), declMethod.Name); + } - //define in/out/ref/name for each of the parameters - for (int i = 0; i < parms.Length ; i++) - method_builder.DefineParameter (i + 1, parms[i].Attributes, parms[i].Name); - ILGenerator ilg = method_builder.GetILGenerator (); - GenHookupMethod (ilg, declMethod, sendMethodCallMethod, interfaceName, declMethod.Name); + } - // Is Event Or Property - if (declMethod.IsEvent () || declMethod.IsProperty ()) - builders[declMethod.Name] = method_builder; - } + public static MethodBuilder CreateMethodBuilder (TypeBuilder typeB, MethodInfo declMethod) + { + ParameterInfo[] parms = declMethod.GetParameters (); - foreach (EventInfo declEvent in iface.GetEvents ()) - { - EventBuilder event_builder = typeB.DefineEvent (declEvent.Name, declEvent.Attributes, declEvent.EventHandlerType); - event_builder.SetAddOnMethod (builders["add_" + declEvent.Name]); - event_builder.SetRemoveOnMethod (builders["remove_" + declEvent.Name]); - } + Type[] parmTypes = new Type[parms.Length]; + for (int i = 0 ; i < parms.Length ; i++) + parmTypes[i] = parms[i].ParameterType; - foreach (PropertyInfo declProp in iface.GetProperties ()) - { - List indexers = new List (); - foreach (ParameterInfo pi in declProp.GetIndexParameters ()) - indexers.Add (pi.ParameterType); - - PropertyBuilder prop_builder = typeB.DefineProperty (declProp.Name, declProp.Attributes, declProp.PropertyType, indexers.ToArray ()); - MethodBuilder mb; - if (builders.TryGetValue ("get_" + declProp.Name, out mb)) - prop_builder.SetGetMethod (mb); - if (builders.TryGetValue ("set_" + declProp.Name, out mb)) - prop_builder.SetSetMethod (mb); - } + MethodAttributes attrs = declMethod.Attributes ^ MethodAttributes.Abstract; + attrs ^= MethodAttributes.NewSlot; + attrs |= MethodAttributes.Final; + MethodBuilder method_builder = typeB.DefineMethod (declMethod.Name, attrs, declMethod.ReturnType, parmTypes); + typeB.DefineMethodOverride (method_builder, declMethod); + + //define in/out/ref/name for each of the parameters + for (int i = 0; i < parms.Length ; i++) + method_builder.DefineParameter (i + 1, parms[i].Attributes, parms[i].Name); + + return method_builder; } public static DynamicMethod GetHookupMethod (EventInfo ei) @@ -246,27 +239,89 @@ public static IEnumerable GetMarshalFields (Type type) return type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); } - public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, MethodInfo invokeMethod, string @interface, string member) + private static void EmitThis (ILGenerator ilg) { - ParameterInfo[] parms = declMethod.GetParameters (); - Type retType = declMethod.ReturnType; - - //the BusObject instance + //the 'this' instance ilg.Emit (OpCodes.Ldarg_0); ilg.Emit (OpCodes.Castclass, typeof (BusObject)); - //interface - ilg.Emit (OpCodes.Ldstr, @interface); + } + + public static void GenHookupProperty (TypeBuilder typeB, PropertyInfo declProp, string @interface, HashSet evaluating) + { + Type[] indexers = declProp.GetIndexParameters ().Select (x => x.ParameterType).ToArray (); + PropertyBuilder prop_builder = typeB.DefineProperty (declProp.Name, + declProp.Attributes, + declProp.PropertyType, + indexers); + + MethodInfo[] sources = new MethodInfo[] { declProp.GetGetMethod (), + declProp.GetSetMethod () }; + + foreach (MethodInfo source in sources) + { + if (null == source) + continue; + + evaluating.Remove (source); + + MethodBuilder meth_builder = CreateMethodBuilder (typeB, source); + ILGenerator ilg = meth_builder.GetILGenerator (); + + bool isGet = typeof(void) != source.ReturnType; + + MethodInfo target = isGet ? sendPropertyGetMethod : sendPropertySetMethod; + + EmitThis (ilg); + + ilg.Emit (OpCodes.Ldstr, @interface); + ilg.Emit (OpCodes.Ldstr, declProp.Name); - //special case event add/remove methods - if (declMethod.IsEvent () && (declMethod.Name.StartsWith ("add_") || declMethod.Name.StartsWith ("remove_"))) { - string[] parts = declMethod.Name.Split (new char[]{'_'}, 2); - string ename = parts[1]; + if (!isGet) + { + ilg.Emit (OpCodes.Ldarg_1); + } + + ilg.Emit (OpCodes.Tailcall); + ilg.Emit (target.IsFinal ? OpCodes.Call : OpCodes.Callvirt, target); + ilg.Emit (OpCodes.Castclass, source.ReturnType); + ilg.Emit (OpCodes.Ret); + + if (isGet) + prop_builder.SetGetMethod (meth_builder); + else + prop_builder.SetSetMethod (meth_builder); + } + } + + public static void GenHookupEvent (TypeBuilder typeB, EventInfo declEvent, string @interface, HashSet evaluating) + { + EventBuilder event_builder = typeB.DefineEvent (declEvent.Name, + declEvent.Attributes, + declEvent.EventHandlerType); + + MethodInfo[] sources = new MethodInfo[] { declEvent.GetAddMethod (), + declEvent.GetRemoveMethod () }; + + foreach (MethodInfo source in sources) + { + if (null == source) + continue; + + evaluating.Remove (source); - bool adding = parts[0] == "add"; + MethodBuilder meth_builder = CreateMethodBuilder (typeB, source); + ILGenerator ilg = meth_builder.GetILGenerator (); - ilg.Emit (OpCodes.Ldstr, ename); + bool adding = sources[0] == source; + + EmitThis (ilg); + + //interface + ilg.Emit (OpCodes.Ldstr, @interface); + + ilg.Emit (OpCodes.Ldstr, declEvent.Name); ilg.Emit (OpCodes.Ldarg_1); @@ -275,16 +330,23 @@ public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, Meth ilg.Emit (OpCodes.Tailcall); ilg.Emit (toggleSignalMethod.IsFinal ? OpCodes.Call : OpCodes.Callvirt, toggleSignalMethod); ilg.Emit (OpCodes.Ret); - return; - } - //property accessor mapping - if (declMethod.IsSpecialName) { - if (member.StartsWith ("get_")) - member = "Get" + member.Substring (4); - else if (member.StartsWith ("set_")) - member = "Set" + member.Substring (4); + if (adding) + event_builder.SetAddOnMethod (meth_builder); + else + event_builder.SetRemoveOnMethod (meth_builder); } + } + + public static void GenHookupMethod (ILGenerator ilg, MethodInfo declMethod, MethodInfo invokeMethod, string @interface, string member) + { + ParameterInfo[] parms = declMethod.GetParameters (); + Type retType = declMethod.ReturnType; + + EmitThis (ilg); + + //interface + ilg.Emit (OpCodes.Ldstr, @interface); //member ilg.Emit (OpCodes.Ldstr, member); From 492157dd0fd5298041b513e1b369bde0e362ffb8 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Sun, 8 Jun 2014 14:42:16 +0100 Subject: [PATCH 4/9] proxy: Improve Get/Set support The previous patch worked great for object references (e.g. strings) but was generating incorrect IL for properties expressed as value types. This patch adds box and unbox OpCodes for the affected properties. --- src/TypeImplementer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index 5b3c8f7..5c222e8 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -281,11 +281,15 @@ public static void GenHookupProperty (TypeBuilder typeB, PropertyInfo declProp, if (!isGet) { ilg.Emit (OpCodes.Ldarg_1); + ilg.Emit (OpCodes.Box, source.GetParameters ()[0].ParameterType); } ilg.Emit (OpCodes.Tailcall); ilg.Emit (target.IsFinal ? OpCodes.Call : OpCodes.Callvirt, target); - ilg.Emit (OpCodes.Castclass, source.ReturnType); + + if (isGet) + ilg.Emit (source.ReturnType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, source.ReturnType); + ilg.Emit (OpCodes.Ret); if (isGet) From 3bf609679c76acbfe95167e2e44b9133dcc85b40 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Sun, 15 Feb 2015 14:03:52 +0000 Subject: [PATCH 5/9] properties: Correct test execution This commit: - Corrects the failing Property unit test; - Adds ExportObject.HandlePropertyCall(MessageContainer); - Adds Mapper.GetInterfaceType(Type, string); - Adds Mapper.GetPublicProperties(Type); - Adds a structure, PropertyCallers to hold the property type and a MethodCaller (or null) for the accessor and mutator; - Adds TypeImplementor.GenPropertyCallers(PropertyInfo) which produces the above structure; - Adds IEnumerable MessageReader.ReadValues() method which will yield objects based on the signature of the received Message the MessageReader was constructed with. --- src/BusObject.cs | 3 +- src/ExportObject.cs | 112 ++++++++++++++++++++++++++++++++-- src/Mapper.cs | 11 ++++ src/Protocol/MessageReader.cs | 7 +++ src/TypeImplementer.cs | 87 ++++++++++++++++++++++++++ 5 files changed, 214 insertions(+), 6 deletions(-) diff --git a/src/BusObject.cs b/src/BusObject.cs index d7827d9..db8af86 100644 --- a/src/BusObject.cs +++ b/src/BusObject.cs @@ -3,6 +3,7 @@ // See COPYING for details using System; +using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; @@ -171,7 +172,7 @@ public object SendPropertyGet (string iface, string property) MessageReader reader = SendMethodCall ("org.freedesktop.DBus.Properties", "Get", "ss", writer, typeof(object), out exception); - return reader.ReadVariant (); + return reader.ReadValues ().FirstOrDefault (); } public void SendPropertySet (string iface, string property, object value) diff --git a/src/ExportObject.cs b/src/ExportObject.cs index df9c195..18939ad 100644 --- a/src/ExportObject.cs +++ b/src/ExportObject.cs @@ -14,16 +14,27 @@ namespace DBus { using Protocol; + internal class PropertyCallers { + public Type Type { get; set; } + public MethodCaller Get { get; set; } + public MethodCaller Set { get; set; } + } + //TODO: perhaps ExportObject should not derive from BusObject internal class ExportObject : BusObject, IDisposable { //maybe add checks to make sure this is not called more than once //it's a bit silly as a property bool isRegistered = false; + + Dictionary propertyInfoCache = new Dictionary (); + Dictionary methodInfoCache = new Dictionary (); static readonly Dictionary mCallers = new Dictionary (); + static readonly Dictionary pCallers = new Dictionary (); + public ExportObject (Connection conn, ObjectPath object_path, object obj) : base (conn, null, object_path) { Object = obj; @@ -79,11 +90,25 @@ public static ExportObject CreateExportObject (Connection conn, ObjectPath objec return new ExportObject (conn, object_path, obj); } + private static string Key (string iface, string member) + { + return string.Format ("{0}.{1}", iface, member); + } + public virtual void HandleMethodCall (MessageContainer method_call) { + switch (method_call.Interface) { + case "org.freedesktop.DBus.Properties": + HandlePropertyCall (method_call); + return; + } + + var cache_key = Key (method_call.Interface, method_call.Member); MethodInfo mi; - if (!methodInfoCache.TryGetValue (method_call.Member, out mi)) - methodInfoCache[method_call.Member] = mi = Mapper.GetMethod (Object.GetType (), method_call); + if (!methodInfoCache.TryGetValue (cache_key, out mi)) { + mi = Mapper.GetMethod (Object.GetType (), method_call); + methodInfoCache [cache_key] = mi; + } if (mi == null) { conn.MaybeSendUnknownMethodError (method_call); @@ -110,6 +135,13 @@ public virtual void HandleMethodCall (MessageContainer method_call) raisedException = e; } + IssueReply (method_call, outSig, retWriter, mi, raisedException); + } + + private void IssueReply (MessageContainer method_call, Signature outSig, MessageWriter retWriter, MethodInfo mi, Exception raisedException) + { + Message msg = method_call.Message; + if (!msg.ReplyExpected) return; @@ -118,6 +150,7 @@ public virtual void HandleMethodCall (MessageContainer method_call) if (raisedException == null) { MessageContainer method_return = new MessageContainer { Type = MessageType.MethodReturn, + Destination = method_call.Sender, ReplySerial = msg.Header.Serial }; replyMsg = method_return.Message; @@ -138,12 +171,81 @@ public virtual void HandleMethodCall (MessageContainer method_call) replyMsg = method_call.CreateError (Mapper.GetInterfaceName (raisedException.GetType ()), raisedException.Message); } - if (method_call.Sender != null) - replyMsg.Header[FieldCode.Destination] = method_call.Sender; - conn.Send (replyMsg); } + private void HandlePropertyCall (MessageContainer method_call) + { + Message msg = method_call.Message; + MessageReader msgReader = new MessageReader (msg); + MessageWriter retWriter = new MessageWriter (); + + object[] args = MessageHelper.GetDynamicValues (msg); + + string face = (string) args [0]; + string name = (string) args [1]; + + PropertyInfo p = GetPropertyInfo (Object.GetType (), face, name); + PropertyCallers pcs = GetPropertyCallers (p); + + MethodCaller pc; + MethodInfo mi; + + switch (method_call.Member) { + case "Set": + pc = pcs.Set; + mi = p.GetSetMethod (); + break; + case "Get": + pc = pcs.Get; + mi = p.GetGetMethod (); + break; + case "GetAll": + throw new NotImplementedException (); + default: + throw new ArgumentException (string.Format ("No such method {0}.{1}", method_call.Interface, method_call.Member)); + } + + if (null == pc || null == mi) { + throw new MissingMethodException (); + } + + Exception raised = null; + try { + pc (Object, msgReader, msg, retWriter); + } catch (Exception e) { + raised = e; + } + + Signature inSig, outSig; + TypeImplementer.SigsForMethod (mi, out inSig, out outSig); + + IssueReply (method_call, outSig, retWriter, mi, raised); + } + + private PropertyInfo GetPropertyInfo (Type type, string @interface, string property) + { + var key = Key (@interface, property); + PropertyInfo pi; + if (!propertyInfoCache.TryGetValue (key, out pi)) { + pi = Mapper.GetPublicProperties(Mapper.GetInterfaceType (type, @interface)).First (x => property == x.Name); + propertyInfoCache [key] = pi; + } + + return pi; + } + + private static PropertyCallers GetPropertyCallers (PropertyInfo pi) + { + PropertyCallers pCaller; + if (!pCallers.TryGetValue (pi, out pCaller)) { + pCaller = TypeImplementer.GenPropertyCallers (pi); + pCallers[pi] = pCaller; + } + + return pCaller; + } + public object Object { get; private set; diff --git a/src/Mapper.cs b/src/Mapper.cs index dcfe170..db95802 100644 --- a/src/Mapper.cs +++ b/src/Mapper.cs @@ -35,6 +35,16 @@ public static string GetArgumentName (ICustomAttributeProvider attrProvider, str return argName; } + public static Type GetInterfaceType(Type type, string iface) + { + return type.GetInterfaces ().FirstOrDefault (x => iface == GetInterfaceName (x)); + } + + public static IEnumerable GetPublicProperties (Type type) + { + return type.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); + } + public static IEnumerable> GetPublicMembers (Type type) { //note that Type.GetInterfaces() returns all interfaces with flattened hierarchy @@ -74,6 +84,7 @@ static IEnumerable GetDeclaredPublicMembers (Type type) public static MethodInfo GetMethod (Type type, MessageContainer method_call) { var mems = Mapper.GetPublicMembers (type).ToArray (); + foreach (var memberForType in mems) { //this could be made more efficient by using the given interface name earlier and avoiding walking through all public interfaces if (method_call.Interface != null) diff --git a/src/Protocol/MessageReader.cs b/src/Protocol/MessageReader.cs index 5779699..c7ba390 100644 --- a/src/Protocol/MessageReader.cs +++ b/src/Protocol/MessageReader.cs @@ -72,6 +72,13 @@ public bool DataAvailable { } } + public IEnumerable ReadValues () + { + for (int i = 0; i < message.Signature.Length; ++i) { + yield return ReadValue (message.Signature[i]); + } + } + public object ReadValue (Type type) { if (type == typeof (void)) diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index 5c222e8..e13433b 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -31,6 +31,7 @@ class TypeImplementer static MethodInfo messageReaderReadArray = typeof (MessageReader).GetMethod ("ReadArray", Type.EmptyTypes); static MethodInfo messageReaderReadDictionary = typeof (MessageReader).GetMethod ("ReadDictionary", Type.EmptyTypes); static MethodInfo messageReaderReadStruct = typeof (MessageReader).GetMethod ("ReadStruct", Type.EmptyTypes); + static MethodInfo messageHelperGetDynamicValues = typeof (MessageHelper).GetMethod ("GetDynamicValues", new [] { typeof (Message) }); static Dictionary writeMethods = new Dictionary (); static Dictionary typeWriters = new Dictionary (); @@ -513,6 +514,92 @@ internal static MethodInfo GetReadMethod (Type t) return null; } + internal static PropertyCallers GenPropertyCallers (PropertyInfo target) + { + var pc = new PropertyCallers { + Type = target.PropertyType, + Get = GenGetMethod (target), + Set = GenSetMethod (target) + }; + + return pc; + } + + internal static MethodCaller GenGetMethod (PropertyInfo target) + { + var mi = target.GetGetMethod (); + + if (null == mi) { + return null; + } + + Type[] parms = new Type[] { typeof (object), typeof (MessageReader), typeof (Message), typeof (MessageWriter) }; + var method = new DynamicMethod ("PropertyGet", typeof(void), parms, typeof(MessageReader)); + + var ilg = method.GetILGenerator (); + + ilg.Emit (OpCodes.Ldarg_0); + ilg.EmitCall (mi.IsFinal ? OpCodes.Call : OpCodes.Callvirt, mi, null); + + LocalBuilder retLocal = ilg.DeclareLocal (mi.ReturnType); + ilg.Emit (OpCodes.Stloc, retLocal); + + ilg.Emit (OpCodes.Ldarg_3); + ilg.Emit (OpCodes.Ldloc, retLocal); + GenWriter (ilg, mi.ReturnType); + + ilg.Emit (OpCodes.Ret); + + return (MethodCaller) method.CreateDelegate (typeof(MethodCaller)); + } + + internal static MethodCaller GenSetMethod (PropertyInfo target) + { + var mi = target.GetSetMethod (); + + if (null == mi) { + return null; + } + + Type[] parms = new Type[] { typeof (object), typeof (MessageReader), typeof (Message), typeof (MessageWriter) }; + var method = new DynamicMethod ("PropertySet", typeof(void), parms, typeof(MessageReader)); + + var ilg = method.GetILGenerator (); + + if (null == messageHelperGetDynamicValues) { + throw new MissingMethodException (typeof(MessageHelper).Name, "GetDynamicValues"); + } + + var args = ilg.DeclareLocal (typeof(object[])); + var arg = ilg.DeclareLocal (typeof(object)); + var v = ilg.DeclareLocal (target.PropertyType); + + ilg.Emit (OpCodes.Ldarg_2); + ilg.Emit (OpCodes.Call, messageHelperGetDynamicValues); + ilg.Emit (OpCodes.Stloc, args); + + ilg.Emit (OpCodes.Ldloc, args); + ilg.Emit (OpCodes.Ldc_I4_2); + ilg.Emit (OpCodes.Ldelem, typeof(object)); + ilg.Emit (OpCodes.Stloc, arg); + + var cast = target.PropertyType.IsValueType + ? OpCodes.Unbox_Any + : OpCodes.Castclass; + + ilg.Emit (OpCodes.Ldloc, arg); + ilg.Emit (cast, target.PropertyType); + ilg.Emit (OpCodes.Stloc, v); + + ilg.Emit (OpCodes.Ldarg_0); + ilg.Emit (OpCodes.Ldloc, v); + ilg.Emit (mi.IsFinal ? OpCodes.Call : OpCodes.Callvirt, mi); + + ilg.Emit (OpCodes.Ret); + + return (MethodCaller) method.CreateDelegate (typeof(MethodCaller)); + } + internal static MethodCaller GenCaller (MethodInfo target) { DynamicMethod hookupMethod = GenReadMethod (target); From bb094af722328cac5dd0ebcb6740048e80a6f58d Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Thu, 19 Feb 2015 22:28:57 +0000 Subject: [PATCH 6/9] sln: Add text formatting My spacing settings convert tabs to spaces, I'm not certain this is the default, but we'll add the specification here to prevent ambiguity. --- dbus-sharp.sln | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dbus-sharp.sln b/dbus-sharp.sln index 6cf24cf..f259203 100644 --- a/dbus-sharp.sln +++ b/dbus-sharp.sln @@ -29,7 +29,18 @@ Global {736160C3-844E-43D9-8106-E492D74D92CD}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = src\dbus-sharp.csproj + Policies = $0 + $0.TextStylePolicy = $1 + $1.FileWidth = 120 + $1.TabsToSpaces = False + $1.inheritsSet = VisualStudio + $1.inheritsScope = text/plain + $1.scope = text/x-csharp + $0.CSharpFormattingPolicy = $2 + $2.AfterDelegateDeclarationParameterComma = True + $2.inheritsSet = Mono + $2.inheritsScope = text/x-csharp + $2.scope = text/x-csharp EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 2a4f7017c60fc1d6a23e989bcb5e40f251550b38 Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Thu, 19 Feb 2015 22:44:21 +0000 Subject: [PATCH 7/9] refactor: Add DBusMemberTable This commit: - Adds a new type, DBusMemberTable which we'll use to formalise the method and property calling apparatus, compared to using four dictionaries; - Moves some of the uses of TypeImplementer from ExportObject to reduce coupling between the two classes; - Renames some badly named methods in TypeImplementer; - Corrects erroneous spacing in previous commits. --- src/ExportObject.cs | 187 +++++++++++++++++++++-------------- src/TypeImplementer.cs | 44 ++++++--- tests/ExportInterfaceTest.cs | 10 +- 3 files changed, 150 insertions(+), 91 deletions(-) diff --git a/src/ExportObject.cs b/src/ExportObject.cs index 18939ad..06f25f7 100644 --- a/src/ExportObject.cs +++ b/src/ExportObject.cs @@ -14,10 +14,82 @@ namespace DBus { using Protocol; - internal class PropertyCallers { - public Type Type { get; set; } + internal class PropertyCall { public MethodCaller Get { get; set; } public MethodCaller Set { get; set; } + public PropertyInfo MetaData { get; set; } + } + + internal class MethodCall { + public Signature Out { get; set; } + public Signature In { get; set; } + public MethodCaller Call { get; set; } + public MethodInfo MetaData { get; set; } + } + + internal class MethodDictionary : Dictionary { } + internal class PropertyDictionary : Dictionary { } + + internal class InterfaceMethods : Dictionary { } + internal class InterfaceProperties : Dictionary { } + + internal class DBusMemberTable { + + public Type ObjectType { get; private set; } + + public DBusMemberTable(Type type) + { + ObjectType = type; + } + + InterfaceMethods Methods = new InterfaceMethods(); + InterfaceProperties Properties = new InterfaceProperties(); + + public MethodCall GetMethodCall (string iface, string name) + { + return Lookup ( + Methods, + iface, + name, + (i, n) => { + Type it = Mapper.GetInterfaceType (ObjectType, i); + MethodInfo mi = it.GetMethod (n); + return TypeImplementer.GenMethodCall (mi); + } + ); + } + + public PropertyCall GetPropertyCall (string iface, string name) + { + return Lookup ( + Properties, + iface, + name, + (i, n) => { + Type it = Mapper.GetInterfaceType(ObjectType, i); + PropertyInfo pi = it.GetProperty(n); + return TypeImplementer.GenPropertyCall (pi); + } + ); + } + + private static V Lookup (TMap1 map, A k1, B k2, Func factory) + where TMap2 : IDictionary, new() + where TMap1 : IDictionary + { + TMap2 first; + if (!map.TryGetValue (k1, out first)) { + map [k1] = first = new TMap2 (); + } + + V value; + if (!first.TryGetValue (k2, out value)) { + first [k2] = value = factory (k1, k2); + } + + return value; + } + } //TODO: perhaps ExportObject should not derive from BusObject @@ -27,13 +99,7 @@ internal class ExportObject : BusObject, IDisposable //it's a bit silly as a property bool isRegistered = false; - Dictionary propertyInfoCache = new Dictionary (); - - Dictionary methodInfoCache = new Dictionary (); - - static readonly Dictionary mCallers = new Dictionary (); - - static readonly Dictionary pCallers = new Dictionary (); + static readonly Dictionary typeMembers = new Dictionary(); public ExportObject (Connection conn, ObjectPath object_path, object obj) : base (conn, null, object_path) { @@ -75,26 +141,16 @@ internal virtual void WriteIntrospect (Introspector intro) intro.WriteType (Object.GetType ()); } - internal static MethodCaller GetMCaller (MethodInfo mi) - { - MethodCaller mCaller; - if (!mCallers.TryGetValue (mi, out mCaller)) { - mCaller = TypeImplementer.GenCaller (mi); - mCallers[mi] = mCaller; - } - return mCaller; - } - public static ExportObject CreateExportObject (Connection conn, ObjectPath object_path, object obj) { + Type type = obj.GetType (); + DBusMemberTable table; + if (!typeMembers.TryGetValue (type, out table)) { + typeMembers [type] = new DBusMemberTable (type); + } return new ExportObject (conn, object_path, obj); } - private static string Key (string iface, string member) - { - return string.Format ("{0}.{1}", iface, member); - } - public virtual void HandleMethodCall (MessageContainer method_call) { switch (method_call.Interface) { @@ -103,26 +159,23 @@ public virtual void HandleMethodCall (MessageContainer method_call) return; } - var cache_key = Key (method_call.Interface, method_call.Member); - MethodInfo mi; - if (!methodInfoCache.TryGetValue (cache_key, out mi)) { - mi = Mapper.GetMethod (Object.GetType (), method_call); - methodInfoCache [cache_key] = mi; + MethodCall mCaller = null; + + try { + mCaller = typeMembers[Object.GetType()].GetMethodCall( + method_call.Interface, + method_call.Member + ); } + catch { /* No Such Member */ } - if (mi == null) { + if (mCaller == null) { conn.MaybeSendUnknownMethodError (method_call); return; } - MethodCaller mCaller; - if (!mCallers.TryGetValue (mi, out mCaller)) { - mCaller = TypeImplementer.GenCaller (mi); - mCallers[mi] = mCaller; - } - - Signature inSig, outSig; - TypeImplementer.SigsForMethod (mi, out inSig, out outSig); + Signature inSig = mCaller.In, + outSig = mCaller.Out; Message msg = method_call.Message; MessageReader msgReader = new MessageReader (msg); @@ -130,12 +183,12 @@ public virtual void HandleMethodCall (MessageContainer method_call) Exception raisedException = null; try { - mCaller (Object, msgReader, msg, retWriter); + mCaller.Call (Object, msgReader, msg, retWriter); } catch (Exception e) { raisedException = e; } - IssueReply (method_call, outSig, retWriter, mi, raisedException); + IssueReply (method_call, outSig, retWriter, mCaller.MetaData, raisedException); } private void IssueReply (MessageContainer method_call, Signature outSig, MessageWriter retWriter, MethodInfo mi, Exception raisedException) @@ -183,31 +236,41 @@ private void HandlePropertyCall (MessageContainer method_call) object[] args = MessageHelper.GetDynamicValues (msg); string face = (string) args [0]; + + if ("GetAll" == method_call.Member) { + // TODO + throw new NotImplementedException (); + } + string name = (string) args [1]; - PropertyInfo p = GetPropertyInfo (Object.GetType (), face, name); - PropertyCallers pcs = GetPropertyCallers (p); + PropertyCall pcs = typeMembers[Object.GetType()].GetPropertyCall ( + face, + name + ); - MethodCaller pc; MethodInfo mi; + MethodCaller pc; + Signature outSig, inSig = method_call.Signature; switch (method_call.Member) { case "Set": + mi = pcs.MetaData.GetSetMethod (); pc = pcs.Set; - mi = p.GetSetMethod (); + outSig = Signature.Empty; break; case "Get": + mi = pcs.MetaData.GetGetMethod (); pc = pcs.Get; - mi = p.GetGetMethod (); + outSig = Signature.GetSig(mi.ReturnType); break; - case "GetAll": - throw new NotImplementedException (); default: throw new ArgumentException (string.Format ("No such method {0}.{1}", method_call.Interface, method_call.Member)); } - if (null == pc || null == mi) { - throw new MissingMethodException (); + if (null == pc) { + conn.MaybeSendUnknownMethodError (method_call); + return; } Exception raised = null; @@ -217,35 +280,9 @@ private void HandlePropertyCall (MessageContainer method_call) raised = e; } - Signature inSig, outSig; - TypeImplementer.SigsForMethod (mi, out inSig, out outSig); - IssueReply (method_call, outSig, retWriter, mi, raised); } - private PropertyInfo GetPropertyInfo (Type type, string @interface, string property) - { - var key = Key (@interface, property); - PropertyInfo pi; - if (!propertyInfoCache.TryGetValue (key, out pi)) { - pi = Mapper.GetPublicProperties(Mapper.GetInterfaceType (type, @interface)).First (x => property == x.Name); - propertyInfoCache [key] = pi; - } - - return pi; - } - - private static PropertyCallers GetPropertyCallers (PropertyInfo pi) - { - PropertyCallers pCaller; - if (!pCallers.TryGetValue (pi, out pCaller)) { - pCaller = TypeImplementer.GenPropertyCallers (pi); - pCallers[pi] = pCaller; - } - - return pCaller; - } - public object Object { get; private set; diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index e13433b..f67a7af 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -514,18 +514,30 @@ internal static MethodInfo GetReadMethod (Type t) return null; } - internal static PropertyCallers GenPropertyCallers (PropertyInfo target) + internal static MethodCall GenMethodCall (MethodInfo target) { - var pc = new PropertyCallers { - Type = target.PropertyType, - Get = GenGetMethod (target), - Set = GenSetMethod (target) + Signature inSig, outSig; + SigsForMethod (target, out inSig, out outSig); + return new MethodCall { + Out = outSig, + In = inSig, + Call = GenCaller (target), + MetaData = target + }; + } + + internal static PropertyCall GenPropertyCall (PropertyInfo target) + { + var pc = new PropertyCall { + Get = GenGetCall (target), + Set = GenSetCall (target), + MetaData = target }; return pc; } - internal static MethodCaller GenGetMethod (PropertyInfo target) + internal static MethodCaller GenGetCall (PropertyInfo target) { var mi = target.GetGetMethod (); @@ -533,15 +545,20 @@ internal static MethodCaller GenGetMethod (PropertyInfo target) return null; } - Type[] parms = new Type[] { typeof (object), typeof (MessageReader), typeof (Message), typeof (MessageWriter) }; + var parms = new Type[] { + typeof (object), + typeof (MessageReader), + typeof (Message), + typeof (MessageWriter) + }; var method = new DynamicMethod ("PropertyGet", typeof(void), parms, typeof(MessageReader)); var ilg = method.GetILGenerator (); + var retLocal = ilg.DeclareLocal (mi.ReturnType); + ilg.Emit (OpCodes.Ldarg_0); ilg.EmitCall (mi.IsFinal ? OpCodes.Call : OpCodes.Callvirt, mi, null); - - LocalBuilder retLocal = ilg.DeclareLocal (mi.ReturnType); ilg.Emit (OpCodes.Stloc, retLocal); ilg.Emit (OpCodes.Ldarg_3); @@ -553,7 +570,7 @@ internal static MethodCaller GenGetMethod (PropertyInfo target) return (MethodCaller) method.CreateDelegate (typeof(MethodCaller)); } - internal static MethodCaller GenSetMethod (PropertyInfo target) + internal static MethodCaller GenSetCall (PropertyInfo target) { var mi = target.GetSetMethod (); @@ -561,7 +578,12 @@ internal static MethodCaller GenSetMethod (PropertyInfo target) return null; } - Type[] parms = new Type[] { typeof (object), typeof (MessageReader), typeof (Message), typeof (MessageWriter) }; + var parms = new Type[] { + typeof (object), + typeof (MessageReader), + typeof (Message), + typeof (MessageWriter) + }; var method = new DynamicMethod ("PropertySet", typeof(void), parms, typeof(MessageReader)); var ilg = method.GetILGenerator (); diff --git a/tests/ExportInterfaceTest.cs b/tests/ExportInterfaceTest.cs index b8891ea..bacb4b8 100644 --- a/tests/ExportInterfaceTest.cs +++ b/tests/ExportInterfaceTest.cs @@ -24,18 +24,18 @@ public class ExportInterfaceTest ObjectPath path = new ObjectPath ("/org/dbussharp/test"); int event_a_count = 0; - + [TestFixtureSetUp] - public void Setup () - { + public void Setup () + { test_server = new Test (); Bus.Session.Register (path, test_server); Assert.AreEqual (Bus.Session.RequestName (bus_name), RequestNameReply.PrimaryOwner); Assert.AreNotEqual (Bus.Session.RequestName (bus_name), RequestNameReply.PrimaryOwner); test = Bus.Session.GetObject (bus_name, path); - } - + } + /// /// /// From 50ea457095c8007c40782d3ec632f87e1142162f Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Sun, 22 Feb 2015 11:24:41 +0000 Subject: [PATCH 8/9] properties: Implement GetAll response This commit adds a prototype GetAll response and accompanying unit test to demonstrate. Unfortunately the unit test may not work on Windows systems since it calls out to the system utility 'dbus-send'. --- src/ExportObject.cs | 32 ++++++++++++++++++++--- src/TypeImplementer.cs | 49 ++++++++++++++++++++++++++++++++++++ tests/ExportInterfaceTest.cs | 36 ++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/ExportObject.cs b/src/ExportObject.cs index 06f25f7..620de6c 100644 --- a/src/ExportObject.cs +++ b/src/ExportObject.cs @@ -28,7 +28,9 @@ internal class MethodCall { } internal class MethodDictionary : Dictionary { } - internal class PropertyDictionary : Dictionary { } + internal class PropertyDictionary : Dictionary { + public MethodCaller All { get; set; } + } internal class InterfaceMethods : Dictionary { } internal class InterfaceProperties : Dictionary { } @@ -59,6 +61,21 @@ public MethodCall GetMethodCall (string iface, string name) ); } + public MethodCaller GetPropertyAllCall (string iface) + { + PropertyDictionary calls; + if (!Properties.TryGetValue(iface, out calls)) { + Properties [iface] = calls = new PropertyDictionary (); + } + + if (null == calls.All) { + Type it = Mapper.GetInterfaceType (ObjectType, iface); + calls.All = TypeImplementer.GenGetAllCall (it); + } + + return calls.All; + } + public PropertyCall GetPropertyCall (string iface, string name) { return Lookup ( @@ -238,8 +255,17 @@ private void HandlePropertyCall (MessageContainer method_call) string face = (string) args [0]; if ("GetAll" == method_call.Member) { - // TODO - throw new NotImplementedException (); + Signature asv = Signature.MakeDict (Signature.StringSig, Signature.VariantSig); + + MethodCaller call = typeMembers [Object.GetType ()].GetPropertyAllCall (face); + + Exception ex = null; + try { + call (Object, msgReader, msg, retWriter); + } catch (Exception e) { ex = e; } + + IssueReply (method_call, asv, retWriter, null, ex); + return; } string name = (string) args [1]; diff --git a/src/TypeImplementer.cs b/src/TypeImplementer.cs index f67a7af..2b3b60a 100644 --- a/src/TypeImplementer.cs +++ b/src/TypeImplementer.cs @@ -622,6 +622,55 @@ internal static MethodCaller GenSetCall (PropertyInfo target) return (MethodCaller) method.CreateDelegate (typeof(MethodCaller)); } + internal static MethodCaller GenGetAllCall (Type @interface) + { + var parms = new Type[] { + typeof (object), + typeof (MessageReader), + typeof (Message), + typeof (MessageWriter) + }; + var method = new DynamicMethod ("PropertyGetAll", typeof(void), parms, typeof(MessageReader)); + + var ilg = method.GetILGenerator (); + var dctT = typeof(Dictionary); + + var strObj = new [] { typeof(string), typeof(object) }; + var dctConstructor = dctT.GetConstructor (new Type[0]); + var dctAdd = dctT.GetMethod ("Add", strObj); + + var accessors = @interface.GetProperties ().Where (x => null != x.GetGetMethod()); + + var dct = ilg.DeclareLocal (dctT); + var val = ilg.DeclareLocal (typeof(object)); + + ilg.Emit (OpCodes.Newobj, dctConstructor); + ilg.Emit (OpCodes.Stloc, dct); + foreach (var property in accessors) { + var mi = property.GetGetMethod (); + + ilg.Emit (OpCodes.Ldarg_0); + ilg.Emit (mi.IsFinal ? OpCodes.Call : OpCodes.Callvirt, mi); + if (mi.ReturnType.IsValueType) { + ilg.Emit (OpCodes.Box, mi.ReturnType); + } + // TODO: Cast object references to typeof(object)? + ilg.Emit (OpCodes.Stloc, val); + + ilg.Emit (OpCodes.Ldloc, dct); + ilg.Emit (OpCodes.Ldstr, property.Name); + ilg.Emit (OpCodes.Ldloc, val); + ilg.Emit (OpCodes.Call, dctAdd); + } + ilg.Emit (OpCodes.Ldarg_3); + ilg.Emit (OpCodes.Ldloc, dct); + GenWriter (ilg, dctT); + + ilg.Emit (OpCodes.Ret); + + return (MethodCaller) method.CreateDelegate (typeof(MethodCaller)); + } + internal static MethodCaller GenCaller (MethodInfo target) { DynamicMethod hookupMethod = GenReadMethod (target); diff --git a/tests/ExportInterfaceTest.cs b/tests/ExportInterfaceTest.cs index bacb4b8..49fd9fc 100644 --- a/tests/ExportInterfaceTest.cs +++ b/tests/ExportInterfaceTest.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using NUnit.Framework; @@ -161,6 +162,41 @@ public void Property () Assert.AreEqual (i + 1, test.SomeProp); } + [Test] + public void AllProperties () + { + var args = string.Format ( + "--dest={0} --type=method_call --print-reply " + + "{1} org.freedesktop.DBus.Properties.GetAll string:{0}", + bus_name, + path + ); + Process dbus = new Process { + StartInfo = new ProcessStartInfo { + FileName = "dbus-send", + Arguments = args, + RedirectStandardOutput = true, + UseShellExecute = false + } + }; + + var iterator = new Thread(() => { do { Bus.Session.Iterate(); } while (true); }); + iterator.Start (); + + if (dbus.Start ()) { + dbus.WaitForExit (); + + string output = dbus.StandardOutput.ReadToEnd (); + Assert.IsNotEmpty (output); + // FIXME: Use a regular expression? + Assert.IsTrue (output.Contains ("SomeProp")); + } else { + Assert.Ignore ("Failed to start test program"); + } + + iterator.Abort (); + } + /// /// /// From 3d713b71a9fc7bd95ec80b476ce185c2ee85c93f Mon Sep 17 00:00:00 2001 From: Nicholas Little Date: Wed, 25 Feb 2015 20:07:15 +0000 Subject: [PATCH 9/9] mapper: Correct GetInterfaceType(Type,string) In recent commits I had neglected to consider that some users of dbus-sharp will be subclassing MarshalByRefObject. This commit corrects that, adding a simple GetHierarchy function to return the type and its ancestors. --- src/Mapper.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Mapper.cs b/src/Mapper.cs index db95802..f0bd87b 100644 --- a/src/Mapper.cs +++ b/src/Mapper.cs @@ -37,7 +37,22 @@ public static string GetArgumentName (ICustomAttributeProvider attrProvider, str public static Type GetInterfaceType(Type type, string iface) { - return type.GetInterfaces ().FirstOrDefault (x => iface == GetInterfaceName (x)); + return type.GetInterfaces() + .Concat(GetHierarchy(type)) + .FirstOrDefault (x => iface == GetInterfaceName (x)); + } + + private static IEnumerable GetHierarchy(Type type) + { + if (IsPublic (type)) { + yield return type; + } + + foreach (var super in GetHierarchy (type.BaseType)) { + if (IsPublic (type)) { + yield return super; + } + } } public static IEnumerable GetPublicProperties (Type type)