Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
properties: Correct test execution
Browse files Browse the repository at this point in the history
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<object> MessageReader.ReadValues() method which will
  yield objects based on the signature of the received Message the
  MessageReader was constructed with.
  • Loading branch information
arfbtwn committed Feb 15, 2015
1 parent 492157d commit 3bf6096
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 6 deletions.
3 changes: 2 additions & 1 deletion src/BusObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See COPYING for details

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
Expand Down Expand Up @@ -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)
Expand Down
112 changes: 107 additions & 5 deletions src/ExportObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, PropertyInfo> propertyInfoCache = new Dictionary<string, PropertyInfo> ();

Dictionary<string, MethodInfo> methodInfoCache = new Dictionary<string, MethodInfo> ();

static readonly Dictionary<MethodInfo, MethodCaller> mCallers = new Dictionary<MethodInfo, MethodCaller> ();

static readonly Dictionary<PropertyInfo, PropertyCallers> pCallers = new Dictionary<PropertyInfo, PropertyCallers> ();

public ExportObject (Connection conn, ObjectPath object_path, object obj) : base (conn, null, object_path)
{
Object = obj;
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions src/Mapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PropertyInfo> GetPublicProperties (Type type)
{
return type.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
}

public static IEnumerable<KeyValuePair<Type, MemberInfo>> GetPublicMembers (Type type)
{
//note that Type.GetInterfaces() returns all interfaces with flattened hierarchy
Expand Down Expand Up @@ -74,6 +84,7 @@ static IEnumerable<MemberInfo> 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)
Expand Down
7 changes: 7 additions & 0 deletions src/Protocol/MessageReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ public bool DataAvailable {
}
}

public IEnumerable<object> 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))
Expand Down
87 changes: 87 additions & 0 deletions src/TypeImplementer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Type,MethodInfo> writeMethods = new Dictionary<Type,MethodInfo> ();
static Dictionary<Type,object> typeWriters = new Dictionary<Type,object> ();
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 3bf6096

Please sign in to comment.