// Copyright 2007 Alp Toker // This software is made available under the MIT License // See COPYING for details using System; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; using System.IO; using System.Xml.Serialization; namespace NDesk.DBus { using Introspection; //FIXME: debug hack public delegate void VoidHandler (); public partial class Connection { //dynamically defines a Type for the proxy object using D-Bus introspection public object GetObject (string bus_name, ObjectPath path) { org.freedesktop.DBus.Introspectable intros = GetObject (bus_name, path); string data = intros.Introspect (); StringReader sr = new StringReader (data); XmlSerializer sz = new XmlSerializer (typeof (Node)); Node node = (Node)sz.Deserialize (sr); Type type = TypeDefiner.Define (node.Interfaces); return GetObject (type, bus_name, path); } //FIXME: debug hack ~Connection () { if (Protocol.Verbose) TypeDefiner.Save (); } } static class TypeDefiner { static AssemblyBuilder asmBdef; static ModuleBuilder modBdef; static void InitHack () { if (asmBdef != null) return; asmBdef = AppDomain.CurrentDomain.DefineDynamicAssembly (new AssemblyName ("Defs"), AssemblyBuilderAccess.RunAndSave); //asmBdef = System.Threading.Thread.GetDomain ().DefineDynamicAssembly (new AssemblyName ("DefAssembly"), AssemblyBuilderAccess.RunAndSave); modBdef = asmBdef.DefineDynamicModule ("Defs.dll", "Defs.dll"); } static uint ifaceId = 0; public static Type Define (Interface[] ifaces) { InitHack (); //Provide a unique interface name //This is a bit ugly string ifaceName = "Aggregate" + (ifaceId++); TypeBuilder typeB = modBdef.DefineType (ifaceName, TypeAttributes.Public | TypeAttributes.Interface); foreach (Interface iface in ifaces) typeB.AddInterfaceImplementation (Define (iface)); return typeB.CreateType (); } static Type Define (Interface iface) { InitHack (); int lastDotPos = iface.Name.LastIndexOf ('.'); string nsName = iface.Name.Substring (0, lastDotPos); string ifaceName = iface.Name.Substring (lastDotPos+1); nsName = nsName.Replace ('.', Type.Delimiter); //using the full interface name is ok, but makes consuming the type from C# difficult since namespaces/Type names may overlap TypeBuilder typeB = modBdef.DefineType (nsName + Type.Delimiter + "I" + ifaceName, TypeAttributes.Public | TypeAttributes.Interface); Define (typeB, iface); return typeB.CreateType (); } public static void Save () { asmBdef.Save ("Defs.dll"); } const MethodAttributes ifaceMethAttr = MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual; public static void Define (TypeBuilder typeB, Interface iface) { foreach (Method declMethod in iface.Methods) { //MethodBuilder method_builder = typeB.DefineMethod (declMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, declMethod.ReturnType, Mapper.GetTypes (ArgDirection.In, declMethod.GetParameters ())); List parms = new List (); if (declMethod.Arguments != null) foreach (Argument arg in declMethod.Arguments) { if (arg.Direction == Introspection.ArgDirection.@in) parms.Add (new Signature (arg.Type).ToType ()); //if (arg.Direction == Introspection.ArgDirection.@out) // parms.Add (new Signature (arg.Type).ToType ().MakeByRefType ()); } Signature outSig = Signature.Empty; //this just takes the last out arg and uses is as the return type if (declMethod.Arguments != null) foreach (Argument arg in declMethod.Arguments) if (arg.Direction == Introspection.ArgDirection.@out) outSig = new Signature (arg.Type); Type retType = outSig == Signature.Empty ? typeof (void) : outSig.ToType (); MethodBuilder method_builder = typeB.DefineMethod (declMethod.Name, ifaceMethAttr, retType, parms.ToArray ()); //define the parameter attributes and names if (declMethod.Arguments != null) { int argNum = 0; foreach (Argument arg in declMethod.Arguments) { if (arg.Direction == Introspection.ArgDirection.@in) method_builder.DefineParameter (++argNum, ParameterAttributes.In, arg.Name); //if (arg.Direction == Introspection.ArgDirection.@out) // method_builder.DefineParameter (++argNum, ParameterAttributes.Out, arg.Name); } } } if (iface.Properties != null) foreach (NDesk.DBus.Introspection.Property prop in iface.Properties) { Type propType = new Signature (prop.Type).ToType (); PropertyBuilder prop_builder = typeB.DefineProperty (prop.Name, PropertyAttributes.None, propType, Type.EmptyTypes); if (prop.Access == propertyAccess.read || prop.Access == propertyAccess.readwrite) prop_builder.SetGetMethod (typeB.DefineMethod ("get_" + prop.Name, ifaceMethAttr | MethodAttributes.SpecialName, propType, Type.EmptyTypes)); if (prop.Access == propertyAccess.write || prop.Access == propertyAccess.readwrite) prop_builder.SetSetMethod (typeB.DefineMethod ("set_" + prop.Name, ifaceMethAttr | MethodAttributes.SpecialName, null, new Type[] {propType})); } if (iface.Signals != null) foreach (NDesk.DBus.Introspection.Signal signal in iface.Signals) { //Type eventType = typeof (EventHandler); Type eventType = typeof (VoidHandler); EventBuilder event_builder = typeB.DefineEvent (signal.Name, EventAttributes.None, eventType); event_builder.SetAddOnMethod (typeB.DefineMethod ("add_" + signal.Name, ifaceMethAttr | MethodAttributes.SpecialName, null, new Type[] {eventType})); event_builder.SetRemoveOnMethod (typeB.DefineMethod ("remove_" + signal.Name, ifaceMethAttr | MethodAttributes.SpecialName, null, new Type[] {eventType})); } //apply InterfaceAttribute ConstructorInfo interfaceAttributeCtor = typeof (InterfaceAttribute).GetConstructor(new Type[] {typeof (string)}); CustomAttributeBuilder cab = new CustomAttributeBuilder (interfaceAttributeCtor, new object[] {iface.Name}); typeB.SetCustomAttribute (cab); } } }