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

Commit

Permalink
refactor: Add DBusMemberTable
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
arfbtwn committed Feb 19, 2015
1 parent bb094af commit 2a4f701
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 91 deletions.
187 changes: 112 additions & 75 deletions src/ExportObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, MethodCall> { }
internal class PropertyDictionary : Dictionary<string, PropertyCall> { }

internal class InterfaceMethods : Dictionary<string, MethodDictionary> { }
internal class InterfaceProperties : Dictionary<string, PropertyDictionary> { }

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<InterfaceMethods, MethodDictionary, string, string, MethodCall> (
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<InterfaceProperties, PropertyDictionary, string, string, PropertyCall> (
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,TMap2,A,B,V> (TMap1 map, A k1, B k2, Func<A,B,V> factory)
where TMap2 : IDictionary<B, V>, new()
where TMap1 : IDictionary<A, TMap2>
{
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
Expand All @@ -27,13 +99,7 @@ internal class ExportObject : BusObject, IDisposable
//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> ();
static readonly Dictionary<Type, DBusMemberTable> typeMembers = new Dictionary<Type, DBusMemberTable>();

public ExportObject (Connection conn, ObjectPath object_path, object obj) : base (conn, null, object_path)
{
Expand Down Expand Up @@ -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) {
Expand All @@ -103,39 +159,36 @@ 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);
MessageWriter retWriter = new MessageWriter ();

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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
44 changes: 33 additions & 11 deletions src/TypeImplementer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,34 +514,51 @@ 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 ();

if (null == mi) {
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);
Expand All @@ -553,15 +570,20 @@ 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 ();

if (null == mi) {
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 ();
Expand Down
10 changes: 5 additions & 5 deletions tests/ExportInterfaceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ITestOne> (bus_name, path);
}
}

/// <summary>
///
/// </summary>
Expand Down

0 comments on commit 2a4f701

Please sign in to comment.