100 changed files with 1224 additions and 12654 deletions
@ -0,0 +1,8 @@ |
|||
namespace Tmds.DBus.SourceGenerator |
|||
{ |
|||
public enum AccessMode |
|||
{ |
|||
Read, |
|||
Write |
|||
} |
|||
} |
|||
@ -1,194 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.IO.MemoryMappedFiles; |
|||
using System.Runtime.InteropServices; |
|||
#if NET6_0_OR_GREATER
|
|||
using System.Runtime.Versioning; |
|||
#endif
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Helper class for determining D-Bus addresses.
|
|||
/// </summary>
|
|||
public static class Address |
|||
{ |
|||
private static bool _systemAddressResolved = false; |
|||
private static string _systemAddress = null; |
|||
private static bool _sessionAddressResolved = false; |
|||
private static string _sessionAddress = null; |
|||
|
|||
/// <summary>
|
|||
/// Address of System bus.
|
|||
/// </summary>
|
|||
public static string System |
|||
{ |
|||
get |
|||
{ |
|||
if (_systemAddressResolved) |
|||
{ |
|||
return _systemAddress; |
|||
} |
|||
|
|||
_systemAddress = GetEnvironmentVariable("DBUS_SYSTEM_BUS_ADDRESS"); |
|||
if (String.IsNullOrEmpty(_systemAddress) && !Environment.IsWindows) |
|||
_systemAddress = "unix:path=/var/run/dbus/system_bus_socket"; |
|||
|
|||
_systemAddressResolved = true; |
|||
return _systemAddress; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Address of Session bus.
|
|||
/// </summary>
|
|||
public static string Session |
|||
{ |
|||
get |
|||
{ |
|||
if (_sessionAddressResolved) |
|||
{ |
|||
return _sessionAddress; |
|||
} |
|||
_sessionAddress = GetEnvironmentVariable("DBUS_SESSION_BUS_ADDRESS"); |
|||
|
|||
if (string.IsNullOrEmpty(_sessionAddress)) |
|||
{ |
|||
if (Environment.IsWindows) |
|||
{ |
|||
_sessionAddress = GetSessionBusAddressFromSharedMemory(); |
|||
} |
|||
else |
|||
{ |
|||
_sessionAddress = GetSessionBusAddressFromX11(); |
|||
} |
|||
} |
|||
_sessionAddressResolved = true; |
|||
return _sessionAddress; |
|||
} |
|||
} |
|||
|
|||
private static string GetEnvironmentVariable(string name) |
|||
{ |
|||
return global::System.Environment.GetEnvironmentVariable(name); |
|||
} |
|||
|
|||
private static string GetSessionBusAddressFromX11() |
|||
{ |
|||
if (!string.IsNullOrEmpty(GetEnvironmentVariable("DISPLAY"))) |
|||
{ |
|||
var display = Interop.XOpenDisplay(null); |
|||
if (display == IntPtr.Zero) |
|||
{ |
|||
return null; |
|||
} |
|||
string username; |
|||
unsafe |
|||
{ |
|||
const int BufLen = 1024; |
|||
byte* stackBuf = stackalloc byte[BufLen]; |
|||
Interop.Passwd passwd; |
|||
IntPtr result; |
|||
Interop.getpwuid_r(Interop.getuid(), out passwd, stackBuf, BufLen, out result); |
|||
if (result != IntPtr.Zero) |
|||
{ |
|||
username = Marshal.PtrToStringAnsi(passwd.Name); |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
var machineId = Environment.MachineId.Replace("-", string.Empty); |
|||
var selectionName = $"_DBUS_SESSION_BUS_SELECTION_{username}_{machineId}"; |
|||
var selectionAtom = Interop.XInternAtom(display, selectionName, false); |
|||
if (selectionAtom == IntPtr.Zero) |
|||
{ |
|||
return null; |
|||
} |
|||
var owner = Interop.XGetSelectionOwner(display, selectionAtom); |
|||
if (owner == IntPtr.Zero) |
|||
{ |
|||
return null; |
|||
} |
|||
var addressAtom = Interop.XInternAtom(display, "_DBUS_SESSION_BUS_ADDRESS", false); |
|||
if (addressAtom == IntPtr.Zero) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
IntPtr actualReturnType; |
|||
IntPtr actualReturnFormat; |
|||
IntPtr nrItemsReturned; |
|||
IntPtr bytesAfterReturn; |
|||
IntPtr propReturn; |
|||
int rv = Interop.XGetWindowProperty(display, owner, addressAtom, 0, 1024, false, (IntPtr)31 /* XA_STRING */, |
|||
out actualReturnType, out actualReturnFormat, out nrItemsReturned, out bytesAfterReturn, out propReturn); |
|||
string address = rv == 0 ? Marshal.PtrToStringAnsi(propReturn) : null; |
|||
if (propReturn != IntPtr.Zero) |
|||
{ |
|||
Interop.XFree(propReturn); |
|||
} |
|||
|
|||
Interop.XCloseDisplay(display); |
|||
|
|||
return address; |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
#if NET6_0_OR_GREATER
|
|||
[SupportedOSPlatform("windows")] |
|||
#endif
|
|||
private static string GetSessionBusAddressFromSharedMemory() |
|||
{ |
|||
string result = ReadSharedMemoryString("DBusDaemonAddressInfo", 255); |
|||
if (String.IsNullOrEmpty(result)) |
|||
result = ReadSharedMemoryString("DBusDaemonAddressInfoDebug", 255); // a DEBUG build of the daemon uses this different address...
|
|||
return result; |
|||
} |
|||
|
|||
#if NET6_0_OR_GREATER
|
|||
[SupportedOSPlatform("windows")] |
|||
#endif
|
|||
private static string ReadSharedMemoryString(string id, long maxlen = -1) |
|||
{ |
|||
MemoryMappedFile shmem; |
|||
try |
|||
{ |
|||
shmem = MemoryMappedFile.OpenExisting(id); |
|||
} |
|||
catch |
|||
{ |
|||
shmem = null; |
|||
} |
|||
if (shmem == null) |
|||
return null; |
|||
MemoryMappedViewStream s = shmem.CreateViewStream(); |
|||
long len = s.Length; |
|||
if (maxlen >= 0 && len > maxlen) |
|||
len = maxlen; |
|||
if (len == 0) |
|||
return string.Empty; |
|||
if (len > Int32.MaxValue) |
|||
len = Int32.MaxValue; |
|||
byte[] bytes = new byte[len]; |
|||
int count = s.Read(bytes, 0, (int)len); |
|||
if (count <= 0) |
|||
return string.Empty; |
|||
|
|||
count = 0; |
|||
while (count < len && bytes[count] != 0) |
|||
count++; |
|||
|
|||
return global::System.Text.Encoding.UTF8.GetString(bytes, 0, count); |
|||
} |
|||
} |
|||
} |
|||
@ -1,301 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Net; |
|||
using System.Text; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Transports; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal class AddressEntry |
|||
{ |
|||
public static AddressEntry[] ParseEntries(string addresses) |
|||
{ |
|||
if (addresses == null) |
|||
throw new ArgumentNullException(nameof(addresses)); |
|||
|
|||
List<AddressEntry> entries = new List<AddressEntry>(); |
|||
|
|||
foreach (string entryStr in addresses.Split(';')) |
|||
entries.Add(AddressEntry.Parse(entryStr)); |
|||
|
|||
return entries.ToArray(); |
|||
} |
|||
|
|||
public string Method; |
|||
public readonly IDictionary<string, string> Properties = new Dictionary<string, string>(); |
|||
public Guid Guid = Guid.Empty; |
|||
|
|||
public override string ToString() |
|||
{ |
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.Append(Method); |
|||
sb.Append(':'); |
|||
|
|||
bool first = true; |
|||
foreach (KeyValuePair<string, string> prop in Properties) |
|||
{ |
|||
if (first) |
|||
first = false; |
|||
else |
|||
sb.Append(','); |
|||
|
|||
sb.Append(prop.Key); |
|||
sb.Append('='); |
|||
sb.Append(Escape(prop.Value)); |
|||
} |
|||
|
|||
if (Guid != Guid.Empty) |
|||
{ |
|||
if (Properties.Count != 0) |
|||
sb.Append(','); |
|||
sb.Append("guid"); |
|||
sb.Append('='); |
|||
sb.Append(Guid.ToString("N")); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
static string Escape(string str) |
|||
{ |
|||
if (str == null) |
|||
return String.Empty; |
|||
|
|||
StringBuilder sb = new StringBuilder(); |
|||
int len = str.Length; |
|||
|
|||
for (int i = 0; i != len; i++) |
|||
{ |
|||
char c = str[i]; |
|||
|
|||
//everything other than the optionally escaped chars _must_ be escaped
|
|||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') |
|||
|| c == '-' || c == '_' || c == '/' || c == '\\' || c == '.') |
|||
sb.Append(c); |
|||
else |
|||
sb.Append(HexEscape(c)); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
static string Unescape(string str) |
|||
{ |
|||
if (str == null) |
|||
return String.Empty; |
|||
|
|||
StringBuilder sb = new StringBuilder(); |
|||
int len = str.Length; |
|||
int i = 0; |
|||
while (i != len) |
|||
{ |
|||
if (IsHexEncoding(str, i)) |
|||
sb.Append(HexUnescape(str, ref i)); |
|||
else |
|||
sb.Append(str[i++]); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public static string HexEscape(char character) |
|||
{ |
|||
if (character > '\xff') |
|||
{ |
|||
throw new ArgumentOutOfRangeException("character"); |
|||
} |
|||
char[] chars = new char[3]; |
|||
int pos = 0; |
|||
EscapeAsciiChar(character, chars, ref pos); |
|||
return new string(chars); |
|||
} |
|||
|
|||
private const char c_DummyChar = (char)0xFFFF; |
|||
private static readonly char[] s_hexUpperChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
|||
|
|||
|
|||
internal static void EscapeAsciiChar(char ch, char[] to, ref int pos) |
|||
{ |
|||
to[pos++] = '%'; |
|||
to[pos++] = s_hexUpperChars[(ch & 0xf0) >> 4]; |
|||
to[pos++] = s_hexUpperChars[ch & 0xf]; |
|||
} |
|||
|
|||
|
|||
public static char HexUnescape(string pattern, ref int index) |
|||
{ |
|||
if ((index < 0) || (index >= pattern.Length)) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("index"); |
|||
} |
|||
if ((pattern[index] == '%') |
|||
&& (pattern.Length - index >= 3)) |
|||
{ |
|||
char ret = EscapedAscii(pattern[index + 1], pattern[index + 2]); |
|||
if (ret != c_DummyChar) |
|||
{ |
|||
index += 3; |
|||
return ret; |
|||
} |
|||
} |
|||
return pattern[index++]; |
|||
} |
|||
public static bool IsHexEncoding(string pattern, int index) |
|||
{ |
|||
if ((pattern.Length - index) < 3) |
|||
{ |
|||
return false; |
|||
} |
|||
if ((pattern[index] == '%') && EscapedAscii(pattern[index + 1], pattern[index + 2]) != c_DummyChar) |
|||
{ |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private static char EscapedAscii(char digit, char next) |
|||
{ |
|||
if (!(((digit >= '0') && (digit <= '9')) |
|||
|| ((digit >= 'A') && (digit <= 'F')) |
|||
|| ((digit >= 'a') && (digit <= 'f')))) |
|||
{ |
|||
return c_DummyChar; |
|||
} |
|||
|
|||
int res = (digit <= '9') |
|||
? ((int)digit - (int)'0') |
|||
: (((digit <= 'F') |
|||
? ((int)digit - (int)'A') |
|||
: ((int)digit - (int)'a')) |
|||
+ 10); |
|||
|
|||
if (!(((next >= '0') && (next <= '9')) |
|||
|| ((next >= 'A') && (next <= 'F')) |
|||
|| ((next >= 'a') && (next <= 'f')))) |
|||
{ |
|||
return c_DummyChar; |
|||
} |
|||
|
|||
return (char)((res << 4) + ((next <= '9') |
|||
? ((int)next - (int)'0') |
|||
: (((next <= 'F') |
|||
? ((int)next - (int)'A') |
|||
: ((int)next - (int)'a')) |
|||
+ 10))); |
|||
} |
|||
|
|||
|
|||
public static AddressEntry Parse(string s) |
|||
{ |
|||
AddressEntry entry = new AddressEntry(); |
|||
|
|||
string[] parts = s.Split(new[] { ':' }, 2); |
|||
|
|||
if (parts.Length < 2) |
|||
throw new FormatException("No colon found"); |
|||
|
|||
entry.Method = parts[0]; |
|||
|
|||
if (parts[1].Length > 0) |
|||
{ |
|||
foreach (string propStr in parts[1].Split(',')) |
|||
{ |
|||
parts = propStr.Split('='); |
|||
|
|||
if (parts.Length < 2) |
|||
throw new FormatException("No equals sign found"); |
|||
if (parts.Length > 2) |
|||
throw new FormatException("Too many equals signs found"); |
|||
|
|||
if (parts[0] == "guid") |
|||
{ |
|||
try |
|||
{ |
|||
entry.Guid = Guid.ParseExact(parts[1], "N"); |
|||
} |
|||
catch |
|||
{ |
|||
throw new FormatException("Invalid guid specified"); |
|||
} |
|||
continue; |
|||
} |
|||
|
|||
entry.Properties[parts[0]] = Unescape(parts[1]); |
|||
} |
|||
} |
|||
|
|||
return entry; |
|||
} |
|||
|
|||
public async Task<EndPoint[]> ResolveAsync(bool listen = false) |
|||
{ |
|||
switch (Method) |
|||
{ |
|||
case "tcp": |
|||
{ |
|||
string host, portStr, family; |
|||
int port = 0; |
|||
|
|||
if (!Properties.TryGetValue ("host", out host)) |
|||
host = "localhost"; |
|||
|
|||
if (!Properties.TryGetValue ("port", out portStr) && !listen) |
|||
throw new FormatException ("No port specified"); |
|||
|
|||
if (portStr != null && !Int32.TryParse (portStr, out port)) |
|||
throw new FormatException("Invalid port: \"" + port + "\""); |
|||
|
|||
if (!Properties.TryGetValue ("family", out family)) |
|||
family = null; |
|||
|
|||
if (string.IsNullOrEmpty(host)) |
|||
{ |
|||
throw new ArgumentException("host"); |
|||
} |
|||
|
|||
IPAddress[] addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false); |
|||
|
|||
var endpoints = new IPEndPoint[addresses.Length]; |
|||
for (int i = 0; i < endpoints.Length; i++) |
|||
{ |
|||
endpoints[i] = new IPEndPoint(addresses[i], port); |
|||
} |
|||
return endpoints; |
|||
} |
|||
case "unix": |
|||
{ |
|||
string path; |
|||
bool abstr; |
|||
|
|||
if (Properties.TryGetValue("path", out path)) |
|||
abstr = false; |
|||
else if (Properties.TryGetValue("abstract", out path)) |
|||
abstr = true; |
|||
else |
|||
throw new ArgumentException("No path specified for UNIX transport"); |
|||
|
|||
if (String.IsNullOrEmpty(path)) |
|||
{ |
|||
throw new ArgumentException("path"); |
|||
} |
|||
|
|||
if (abstr) |
|||
{ |
|||
path = (char)'\0' + path; |
|||
} |
|||
|
|||
return new EndPoint[] { new UnixDomainSocketEndPoint(path) }; |
|||
} |
|||
default: |
|||
throw new NotSupportedException("Transport method \"" + Method + "\" not supported"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Indicates the method return type or signal type represents a single D-Bus argument.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false, Inherited = true)] |
|||
public sealed class ArgumentAttribute : Attribute |
|||
{ |
|||
/// <summary>
|
|||
/// Name of the argument.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Creates an instance of the ArgumentAttribute with the specified name.
|
|||
/// </summary>
|
|||
/// <param name="name">Name of the argument.</param>
|
|||
public ArgumentAttribute(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates an instance of the ArgumentAttribute.
|
|||
/// </summary>
|
|||
public ArgumentAttribute() |
|||
{ |
|||
Name = "value"; |
|||
} |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Options that configure the behavior of a Connection to a remote peer.
|
|||
/// </summary>
|
|||
public class ClientConnectionOptions : ConnectionOptions |
|||
{ |
|||
private string _address; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new Connection with a specific address.
|
|||
/// </summary>
|
|||
/// <param name="address">Address of the D-Bus peer.</param>
|
|||
public ClientConnectionOptions(string address) |
|||
{ |
|||
if (address == null) |
|||
throw new ArgumentNullException(nameof(address)); |
|||
_address = address; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base constructor for derived types.
|
|||
/// </summary>
|
|||
protected ClientConnectionOptions() |
|||
{} |
|||
|
|||
/// <summary>
|
|||
/// Automatically connect and re-connect the Connection.
|
|||
/// </summary>
|
|||
public bool AutoConnect { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Sets up tunnel/connects to the remote peer.
|
|||
/// </summary>
|
|||
protected internal virtual Task<ClientSetupResult> SetupAsync() |
|||
{ |
|||
return Task.FromResult( |
|||
new ClientSetupResult |
|||
{ |
|||
ConnectionAddress = _address, |
|||
SupportsFdPassing = true, |
|||
UserId = Environment.UserId |
|||
}); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Action to clean up resources created during succesfull execution of SetupAsync.
|
|||
/// </summary>
|
|||
protected internal virtual void Teardown(object token) |
|||
{} |
|||
|
|||
/// <summary>
|
|||
/// Run Task continuations asynchronously.
|
|||
/// </summary>
|
|||
public bool RunContinuationsAsynchronously { get; set; } |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Result of ClientConnectionOptions.SetupAsync
|
|||
/// </summary>
|
|||
public class ClientSetupResult |
|||
{ |
|||
/// <summary>
|
|||
/// Address of the D-Bus peer.
|
|||
/// </summary>
|
|||
public string ConnectionAddress { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Object passed to ConnectionOptions.Teardown.
|
|||
/// </summary>
|
|||
public object TeardownToken { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Authentication User ID (Linux UID).
|
|||
/// </summary>
|
|||
public string UserId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether the connection supports Fd passing.
|
|||
/// </summary>
|
|||
public bool SupportsFdPassing { get; set; } |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Generic file descriptor SafeHandle.
|
|||
/// </summary>
|
|||
public class CloseSafeHandle : SafeHandle |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new CloseSafeHandle.
|
|||
/// </summary>
|
|||
/// <param name="preexistingHandle">An IntPtr object that represents the pre-existing handle to use.</param>
|
|||
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during the finalization phase; <c>false</c> to prevent reliable release.</param>
|
|||
public CloseSafeHandle(IntPtr preexistingHandle, bool ownsHandle) |
|||
: base(new IntPtr(-1), ownsHandle) |
|||
{ |
|||
SetHandle(preexistingHandle); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the handle is invalid.
|
|||
/// </summary>
|
|||
public override bool IsInvalid |
|||
{ |
|||
get { return handle == new IntPtr(-1); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// When overridden in a derived class, executes the code required to free the handle.
|
|||
/// </summary>
|
|||
protected override bool ReleaseHandle() |
|||
{ |
|||
return close(handle.ToInt32()) == 0; |
|||
} |
|||
|
|||
[DllImport("libc", SetLastError = true)] |
|||
internal static extern int close(int fd); |
|||
} |
|||
} |
|||
@ -1,261 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
static internal class ArgTypeInspector |
|||
{ |
|||
private static readonly Type s_idbusObjectType = typeof(IDBusObject); |
|||
private static readonly Type s_idictionaryGenericType = typeof(IDictionary<,>); |
|||
private static readonly Type s_keyValueGenericPairType = typeof(KeyValuePair<,>); |
|||
private static readonly Type s_ienumerableGenricType = typeof(IEnumerable<>); |
|||
private static readonly Type s_ienumerableType = typeof(System.Collections.IEnumerable); |
|||
private static readonly Type s_objectType = typeof(Object); |
|||
private static readonly Type s_valueType = typeof(ValueType); |
|||
private static readonly Type s_ilistGenricType = typeof(IList<>); |
|||
private static readonly Type s_icollectionGenricType = typeof(ICollection<>); |
|||
private static readonly Type s_stringObjectKeyValuePairType = typeof(KeyValuePair<string, object>); |
|||
private static readonly Type[] s_valueTupleTypes = new [] { |
|||
typeof(ValueTuple<>), |
|||
typeof(ValueTuple<,>), |
|||
typeof(ValueTuple<,,>), |
|||
typeof(ValueTuple<,,,>), |
|||
typeof(ValueTuple<,,,,>), |
|||
typeof(ValueTuple<,,,,,>), |
|||
typeof(ValueTuple<,,,,,,>), |
|||
typeof(ValueTuple<,,,,,,,>) |
|||
}; |
|||
private static readonly IComparer<FieldInfo> s_valueTupleFieldComparer = new StructFieldInfoComparer(true); |
|||
private static readonly IComparer<FieldInfo> s_otherFieldComparer = new StructFieldInfoComparer(false); |
|||
|
|||
public static bool IsDBusObjectType(Type type, bool isCompileTimeType) |
|||
{ |
|||
if (type == s_idbusObjectType) |
|||
{ |
|||
return true; |
|||
} |
|||
var typeInfo = type.GetTypeInfo(); |
|||
if (isCompileTimeType) |
|||
{ |
|||
return typeInfo.IsInterface && typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType); |
|||
} |
|||
else |
|||
{ |
|||
return typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType); |
|||
} |
|||
} |
|||
|
|||
public static bool IsSafeHandleType(Type type) |
|||
{ |
|||
return type.GetTypeInfo().IsSubclassOf(typeof(SafeHandle)); |
|||
} |
|||
|
|||
public enum EnumerableType |
|||
{ |
|||
NotEnumerable, |
|||
Enumerable, // IEnumerable
|
|||
EnumerableKeyValuePair, // IEnumerable<KeyValuePair>
|
|||
GenericDictionary, // IDictionary
|
|||
AttributeDictionary // AttributeDictionary
|
|||
} |
|||
|
|||
public static EnumerableType InspectEnumerableType(Type type, out Type elementType, bool isCompileTimeType) |
|||
{ |
|||
elementType = null; |
|||
var typeInfo = type.GetTypeInfo(); |
|||
|
|||
if (isCompileTimeType) |
|||
{ |
|||
if (typeInfo.IsArray) |
|||
{ |
|||
elementType = typeInfo.GetElementType(); |
|||
return InspectElementType(elementType); |
|||
} |
|||
if (typeInfo.IsInterface && typeInfo.IsGenericType) |
|||
{ |
|||
var genericTypeDefinition = typeInfo.GetGenericTypeDefinition(); |
|||
if (genericTypeDefinition == s_idictionaryGenericType) |
|||
{ |
|||
elementType = s_keyValueGenericPairType.MakeGenericType(typeInfo.GenericTypeArguments); |
|||
return EnumerableType.GenericDictionary; |
|||
} |
|||
else if (genericTypeDefinition == s_ienumerableGenricType || |
|||
genericTypeDefinition == s_ilistGenricType || |
|||
genericTypeDefinition == s_icollectionGenricType) |
|||
{ |
|||
elementType = typeInfo.GenericTypeArguments[0]; |
|||
return InspectElementType(elementType); |
|||
} |
|||
else |
|||
{ |
|||
return EnumerableType.NotEnumerable; |
|||
} |
|||
} |
|||
var dictionaryAttribute = typeInfo.GetCustomAttribute<DictionaryAttribute>(false); |
|||
if (dictionaryAttribute != null) |
|||
{ |
|||
elementType = s_stringObjectKeyValuePairType; |
|||
return EnumerableType.AttributeDictionary; |
|||
} |
|||
return EnumerableType.NotEnumerable; |
|||
} |
|||
else |
|||
{ |
|||
if (typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType)) |
|||
{ |
|||
return EnumerableType.NotEnumerable; |
|||
} |
|||
|
|||
var dictionaryAttribute = typeInfo.GetCustomAttribute<DictionaryAttribute>(false); |
|||
if (dictionaryAttribute != null) |
|||
{ |
|||
elementType = s_stringObjectKeyValuePairType; |
|||
return EnumerableType.AttributeDictionary; |
|||
} |
|||
|
|||
if (!typeInfo.ImplementedInterfaces.Contains(s_ienumerableType)) |
|||
{ |
|||
return EnumerableType.NotEnumerable; |
|||
} |
|||
|
|||
var enumerableTypes = from interf in typeInfo.ImplementedInterfaces |
|||
let interfTypeinfo = interf.GetTypeInfo() |
|||
where interfTypeinfo.IsGenericType && interfTypeinfo.GetGenericTypeDefinition() == s_ienumerableGenricType |
|||
select interfTypeinfo; |
|||
|
|||
var enumerableCount = enumerableTypes.Count(); |
|||
|
|||
if (enumerableCount == 1) |
|||
{ |
|||
elementType = enumerableTypes.First().GenericTypeArguments[0]; |
|||
return InspectElementType(elementType); |
|||
} |
|||
else |
|||
{ |
|||
throw new ArgumentException($"Cannot determine element type of enumerable type '$type.FullName'"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static bool IsStructType(Type type) |
|||
{ |
|||
bool isValueTuple; |
|||
return IsStructType(type, out isValueTuple); |
|||
} |
|||
|
|||
public static bool IsStructType(Type type, out bool isValueTuple) |
|||
{ |
|||
isValueTuple = false; |
|||
var typeInfo = type.GetTypeInfo(); |
|||
if (typeInfo.IsPointer || |
|||
typeInfo.IsInterface || |
|||
typeInfo.IsArray || |
|||
typeInfo.IsPrimitive || |
|||
typeInfo.IsAbstract) |
|||
{ |
|||
return false; |
|||
} |
|||
if (IsValueTuple(typeInfo)) |
|||
{ |
|||
isValueTuple = true; |
|||
return true; |
|||
} |
|||
else if (!typeInfo.IsLayoutSequential) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (typeInfo.ImplementedInterfaces.Contains(s_ienumerableType)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (typeInfo.BaseType != s_objectType && |
|||
typeInfo.BaseType != s_valueType) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var fields = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); |
|||
if (!fields.Any()) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public static FieldInfo[] GetStructFields(Type structType, bool isValueTuple) |
|||
{ |
|||
var fields = structType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); |
|||
Array.Sort<FieldInfo, object>(fields, null, isValueTuple ? s_valueTupleFieldComparer : s_otherFieldComparer); |
|||
return fields; |
|||
} |
|||
|
|||
private static EnumerableType InspectElementType(Type elementType) |
|||
{ |
|||
var typeInfo = elementType.GetTypeInfo(); |
|||
bool isKeyValuePair = typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == s_keyValueGenericPairType; |
|||
if (isKeyValuePair) |
|||
{ |
|||
return EnumerableType.EnumerableKeyValuePair; |
|||
} |
|||
else |
|||
{ |
|||
return EnumerableType.Enumerable; |
|||
} |
|||
} |
|||
|
|||
private static bool IsValueTuple(TypeInfo typeInfo) |
|||
{ |
|||
if (typeInfo.IsGenericType) |
|||
{ |
|||
var genericTypeDefinition = typeInfo.GetGenericTypeDefinition(); |
|||
return s_valueTupleTypes.Contains(genericTypeDefinition); |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private class StructFieldInfoComparer : IComparer<FieldInfo> |
|||
{ |
|||
private bool _isValueTypleComparer; |
|||
public StructFieldInfoComparer(bool isValueTypleComparer) |
|||
{ |
|||
_isValueTypleComparer = isValueTypleComparer; |
|||
} |
|||
|
|||
public int Compare(FieldInfo x, FieldInfo y) |
|||
{ |
|||
if (_isValueTypleComparer) |
|||
{ |
|||
// ValueTuples are not layout sequentially
|
|||
// The fields are named Item[1-7], Rest
|
|||
if (x.Name.Length == 4) |
|||
{ |
|||
return 1; |
|||
} |
|||
return x.Name[x.Name.Length - 1].CompareTo(y.Name[y.Name.Length - 1]); |
|||
} |
|||
else |
|||
{ |
|||
return x.MetadataToken.CompareTo(y.MetadataToken); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class ArgumentDescription |
|||
{ |
|||
public ArgumentDescription(string name, Signature signature, Type type) |
|||
{ |
|||
Name = name; |
|||
Signature = signature; |
|||
Type = type; |
|||
} |
|||
|
|||
public string Name { get; } |
|||
public Signature Signature { get; } |
|||
public Type Type { get; } |
|||
} |
|||
} |
|||
@ -1,310 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class DBusAdapter |
|||
{ |
|||
internal delegate Task<Message> MethodCallHandler(object o, Message methodCall, IProxyFactory factory); |
|||
|
|||
private enum State |
|||
{ |
|||
Registering, |
|||
Registered, |
|||
Unregistered |
|||
} |
|||
|
|||
private readonly object _gate = new object(); |
|||
private readonly DBusConnection _connection; |
|||
private readonly IProxyFactory _factory; |
|||
private readonly ObjectPath2 _objectPath2; |
|||
private readonly SynchronizationContext _synchronizationContext; |
|||
protected internal string _typeIntrospection; |
|||
protected internal readonly Dictionary<string, MethodCallHandler> _methodHandlers; |
|||
protected internal readonly object _object; |
|||
|
|||
private State _state; |
|||
private List<IDisposable> _signalWatchers; |
|||
|
|||
protected DBusAdapter(DBusConnection connection, ObjectPath2 objectPath2, object o, IProxyFactory factory, SynchronizationContext synchronizationContext) |
|||
{ |
|||
_connection = connection; |
|||
_objectPath2 = objectPath2; |
|||
_object = o; |
|||
_state = State.Registering; |
|||
_factory = factory; |
|||
_synchronizationContext = synchronizationContext; |
|||
_methodHandlers = new Dictionary<string, MethodCallHandler>(); |
|||
_methodHandlers.Add(GetMethodLookupKey("org.freedesktop.DBus.Introspectable", "Introspect", null), HandleIntrospect); |
|||
} |
|||
|
|||
public ObjectPath2 Path2 => _objectPath2; |
|||
|
|||
public void Unregister() |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (_state == State.Unregistered) |
|||
{ |
|||
return; |
|||
} |
|||
_state = State.Unregistered; |
|||
if (_signalWatchers != null) |
|||
{ |
|||
foreach (var disposable in _signalWatchers) |
|||
{ |
|||
disposable.Dispose(); |
|||
} |
|||
_signalWatchers = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void CompleteRegistration() |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (_state == State.Registering) |
|||
{ |
|||
_state = State.Registered; |
|||
} |
|||
else if (_state == State.Unregistered) |
|||
{ |
|||
throw new InvalidOperationException("The object has been unregistered"); |
|||
} |
|||
else if (_state == State.Registered) |
|||
{ |
|||
throw new InvalidOperationException("The object has already been registered"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public async Task WatchSignalsAsync() |
|||
{ |
|||
var tasks = StartWatchingSignals(); |
|||
IEnumerable<IDisposable> signalDisposables = null; |
|||
|
|||
try |
|||
{ |
|||
await Task.WhenAll(tasks).ConfigureAwait(false); |
|||
signalDisposables = tasks.Select(task => task.Result); |
|||
|
|||
if (signalDisposables.Contains(null)) |
|||
{ |
|||
throw new InvalidOperationException("One or more Watch-methods returned a null IDisposable"); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
foreach (var task in tasks) |
|||
{ |
|||
try |
|||
{ |
|||
var disposable = await task.ConfigureAwait(false); |
|||
disposable?.Dispose(); |
|||
} |
|||
finally |
|||
{ } |
|||
} |
|||
throw; |
|||
} |
|||
|
|||
lock (_gate) |
|||
{ |
|||
if (_state == State.Registering) |
|||
{ |
|||
_signalWatchers = new List<IDisposable>(); |
|||
_signalWatchers.AddRange(signalDisposables); |
|||
} |
|||
else if (_state == State.Unregistered) |
|||
{ |
|||
foreach (var disposable in signalDisposables) |
|||
{ |
|||
disposable.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static protected internal string GetMethodLookupKey(string iface, string member, Signature? signature) |
|||
{ |
|||
return $"{iface}.{member}.{signature?.Value}"; |
|||
} |
|||
|
|||
static protected internal string GetPropertyLookupKey(string iface, string member, Signature? signature) |
|||
{ |
|||
return $"org.freedesktop.DBus.Properties.{iface}.{member}.{signature?.Value}"; |
|||
} |
|||
|
|||
static protected internal string GetPropertyAddKey(string iface, string member, Signature? signature) |
|||
{ |
|||
return $"org.freedesktop.DBus.Properties.{iface}.{member}.s{signature?.Value}"; |
|||
} |
|||
|
|||
public async Task<Message> HandleMethodCall(Message methodCall) |
|||
{ |
|||
var key = GetMethodLookupKey(methodCall.Header.Interface, methodCall.Header.Member, methodCall.Header.Signature); |
|||
MethodCallHandler handler = null; |
|||
if (!_methodHandlers.TryGetValue(key, out handler)) |
|||
{ |
|||
if (methodCall.Header.Interface == "org.freedesktop.DBus.Properties") |
|||
{ |
|||
MessageReader reader = new MessageReader(methodCall, null); |
|||
var interf = reader.ReadString(); |
|||
key = GetPropertyLookupKey(interf, methodCall.Header.Member, methodCall.Header.Signature); |
|||
_methodHandlers.TryGetValue(key, out handler); |
|||
} |
|||
} |
|||
if (handler != null) |
|||
{ |
|||
if (_synchronizationContext == null) |
|||
{ |
|||
try |
|||
{ |
|||
return await handler(_object, methodCall, _factory).ConfigureAwait(false); |
|||
} |
|||
catch (DBusException be) |
|||
{ |
|||
return MessageHelper.ConstructErrorReply(methodCall, be.ErrorName, be.ErrorMessage); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
return MessageHelper.ConstructErrorReply(methodCall, e.GetType().FullName, e.Message); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var tcs = new TaskCompletionSource<Message>(); |
|||
_synchronizationContext.Post(async _ => { |
|||
Message reply; |
|||
try |
|||
{ |
|||
reply = await handler(_object, methodCall, _factory).ConfigureAwait(false); |
|||
} |
|||
catch (DBusException be) |
|||
{ |
|||
reply = MessageHelper.ConstructErrorReply(methodCall, be.ErrorName, be.ErrorMessage); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
reply = MessageHelper.ConstructErrorReply(methodCall, e.GetType().FullName, e.Message); |
|||
} |
|||
tcs.SetResult(reply); |
|||
}, null); |
|||
return await tcs.Task.ConfigureAwait(false); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var errorMessage = String.Format("Method \"{0}\" with signature \"{1}\" on interface \"{2}\" doesn't exist", |
|||
methodCall.Header.Member, |
|||
methodCall.Header.Signature?.Value, |
|||
methodCall.Header.Interface); |
|||
|
|||
var replyMessage = MessageHelper.ConstructErrorReply(methodCall, "org.freedesktop.DBus.Error.UnknownMethod", errorMessage); |
|||
|
|||
return replyMessage; |
|||
} |
|||
} |
|||
|
|||
protected internal virtual Task<IDisposable>[] StartWatchingSignals() |
|||
{ |
|||
return Array.Empty<Task<IDisposable>>(); |
|||
} |
|||
|
|||
protected internal void EmitVoidSignal(string interfaceName, string signalName) |
|||
{ |
|||
EmitNonVoidSignal(interfaceName, signalName, null, null); |
|||
} |
|||
|
|||
protected internal void EmitNonVoidSignal(string iface, string member, Signature? inSigStr, MessageWriter writer) |
|||
{ |
|||
if (!IsRegistered) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Message signalMsg = new Message( |
|||
new Header(MessageType.Signal) |
|||
{ |
|||
Path = _objectPath2, |
|||
Interface = iface, |
|||
Member = member, |
|||
Signature = inSigStr |
|||
}, |
|||
writer?.ToArray(), |
|||
writer?.UnixFds |
|||
); |
|||
|
|||
_connection.EmitSignal(signalMsg); |
|||
} |
|||
|
|||
protected internal async Task<Message> CreateNonVoidReply<T>(Message methodCall, Task<T> resultTask, Action<MessageWriter, T> writeResult, Signature? outSignature) |
|||
{ |
|||
uint serial = methodCall.Header.Serial; |
|||
|
|||
T result = await resultTask.ConfigureAwait(false); |
|||
MessageWriter retWriter = new MessageWriter(); |
|||
writeResult(retWriter, result); |
|||
|
|||
Message replyMsg = new Message( |
|||
new Header(MessageType.MethodReturn) |
|||
{ |
|||
Signature = outSignature |
|||
}, |
|||
retWriter.ToArray(), |
|||
retWriter.UnixFds |
|||
); |
|||
return replyMsg; |
|||
} |
|||
|
|||
protected internal async Task<Message> CreateVoidReply(Message methodCall, Task task) |
|||
{ |
|||
uint serial = methodCall.Header.Serial; |
|||
await task.ConfigureAwait(false); |
|||
var replyMsg = new Message( |
|||
new Header(MessageType.MethodReturn), |
|||
body: null, |
|||
unixFds: null |
|||
); |
|||
return replyMsg; |
|||
} |
|||
|
|||
private Task<Message> HandleIntrospect(object o, Message methodCall, IProxyFactory factory) |
|||
{ |
|||
IntrospectionWriter writer = new IntrospectionWriter(); |
|||
|
|||
writer.WriteDocType(); |
|||
writer.WriteNodeStart(_objectPath2.Value); |
|||
writer.WriteLiteral(_typeIntrospection); |
|||
foreach (var child in _connection.GetChildNames(_objectPath2)) |
|||
{ |
|||
writer.WriteChildNode(child); |
|||
} |
|||
writer.WriteNodeEnd(); |
|||
|
|||
var xml = writer.ToString(); |
|||
var response = MessageHelper.ConstructReply(methodCall, xml); |
|||
return Task.FromResult(response); |
|||
} |
|||
|
|||
private bool IsRegistered |
|||
{ |
|||
get |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
return _state == State.Registered; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,417 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Reflection.Emit; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class DBusAdapterTypeBuilder |
|||
{ |
|||
private static readonly ConstructorInfo s_messageWriterConstructor = typeof(MessageWriter).GetConstructor(Type.EmptyTypes); |
|||
private static readonly Type[] s_dbusAdaptorConstructorParameterTypes = new Type[] { typeof(DBusConnection), typeof(ObjectPath2), typeof(object), typeof(IProxyFactory), typeof(SynchronizationContext) }; |
|||
private static readonly ConstructorInfo s_baseConstructor = typeof(DBusAdapter).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, s_dbusAdaptorConstructorParameterTypes); |
|||
private static readonly ConstructorInfo s_methodHandlerConstructor = typeof(DBusAdapter.MethodCallHandler).GetConstructors()[0]; |
|||
private static readonly ConstructorInfo s_signatureConstructor = typeof(Signature).GetConstructor(new Type[] { typeof(string) }); |
|||
private static readonly ConstructorInfo s_nullableSignatureConstructor = typeof(Signature?).GetConstructor(new Type[] { typeof(Signature) }); |
|||
private static readonly ConstructorInfo s_messageReaderConstructor = typeof(MessageReader).GetConstructor(new Type[] { typeof(Message), typeof(IProxyFactory) }); |
|||
private static readonly FieldInfo s_methodDictionaryField = typeof(DBusAdapter).GetField(nameof(DBusAdapter._methodHandlers), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly FieldInfo s_objectField = typeof(DBusAdapter).GetField(nameof(DBusAdapter._object), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_methodDictionaryAdd = typeof(Dictionary<string, DBusAdapter.MethodCallHandler>).GetMethod("Add", new Type[] { typeof(string), typeof(DBusAdapter.MethodCallHandler) }); |
|||
private static readonly MethodInfo s_startWatchingSignals = typeof(DBusAdapter).GetMethod(nameof(DBusAdapter.StartWatchingSignals), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_emitVoidSignal = typeof(DBusAdapter).GetMethod(nameof(DBusAdapter.EmitVoidSignal), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_emitNonVoidSignal = typeof(DBusAdapter).GetMethod(nameof(DBusAdapter.EmitNonVoidSignal), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_createNonVoidReply = typeof(DBusAdapter).GetMethod(nameof(DBusAdapter.CreateNonVoidReply), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_createVoidReply = typeof(DBusAdapter).GetMethod(nameof(DBusAdapter.CreateVoidReply), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_readerSkipString = typeof(MessageReader).GetMethod(nameof(MessageReader.SkipString), BindingFlags.Instance | BindingFlags.Public); |
|||
private static readonly MethodInfo s_writerWriteString = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteString), BindingFlags.Instance | BindingFlags.Public); |
|||
private static readonly MethodInfo s_writerSetSkipNextStructPadding = typeof(MessageWriter).GetMethod(nameof(MessageWriter.SetSkipNextStructPadding), BindingFlags.Instance | BindingFlags.Public); |
|||
private static readonly FieldInfo s_setTypeIntrospectionField = typeof(DBusAdapter).GetField(nameof(DBusAdapter._typeIntrospection), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly Type s_taskOfMessageType = typeof(Task<Message>); |
|||
private static readonly Type s_nullableSignatureType = typeof(Signature?); |
|||
private static readonly Type s_action2GenericType = typeof(Action<,>); |
|||
private static readonly Type s_messageWriterType = typeof(MessageWriter); |
|||
private static readonly Type s_messageReaderType = typeof(MessageReader); |
|||
// private static readonly Type s_stringType = typeof(string);
|
|||
private static readonly Type[] s_methodHandlerParameterTypes = new[] { typeof(object), typeof(Message), typeof(IProxyFactory) }; |
|||
|
|||
private readonly ModuleBuilder _moduleBuilder; |
|||
private TypeBuilder _typeBuilder; |
|||
|
|||
public DBusAdapterTypeBuilder(ModuleBuilder moduleBuilder) |
|||
{ |
|||
_moduleBuilder = moduleBuilder; |
|||
} |
|||
|
|||
public TypeInfo Build(Type objectType) |
|||
{ |
|||
if (_typeBuilder != null) |
|||
{ |
|||
throw new InvalidOperationException("Type has already been built."); |
|||
} |
|||
|
|||
var parentType = typeof(DBusAdapter); |
|||
var adapterName = objectType.FullName + "Adapter"; |
|||
_typeBuilder = _moduleBuilder.DefineType(adapterName, TypeAttributes.Class | TypeAttributes.Public, parentType); |
|||
|
|||
var description = TypeDescription.DescribeObject(objectType); |
|||
|
|||
ImplementConstructor(description); |
|||
ImplementStartWatchingSignals(description.Interfaces); |
|||
|
|||
return _typeBuilder.CreateTypeInfo(); |
|||
} |
|||
|
|||
private void ImplementStartWatchingSignals(IList<InterfaceDescription> interfaces) |
|||
{ |
|||
var signalCount = interfaces.Aggregate(0, (count, iface) => count + |
|||
(iface.Signals?.Count ?? 0) + |
|||
((iface.PropertiesChangedSignal != null) ? 1 : 0)); |
|||
if (signalCount == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var method = _typeBuilder.OverrideAbstractMethod(s_startWatchingSignals); |
|||
var ilg = method.GetILGenerator(); |
|||
|
|||
// signals = new Task<IDisposable>[signalCount];
|
|||
ilg.Emit(OpCodes.Ldc_I4, signalCount); |
|||
ilg.Emit(OpCodes.Newarr, typeof(Task<IDisposable>)); |
|||
|
|||
var idx = 0; |
|||
foreach (var dbusInterface in interfaces) |
|||
{ |
|||
IEnumerable<SignalDescription> signals = dbusInterface.Signals ?? Array.Empty<SignalDescription>(); |
|||
|
|||
if (dbusInterface.PropertiesChangedSignal != null) |
|||
{ |
|||
signals = signals.Concat(new[] { dbusInterface.PropertiesChangedSignal }); |
|||
} |
|||
|
|||
foreach (var signal in signals) |
|||
{ |
|||
// signals[i] = Watch((IDbusInterface)this, SendSignalAction)
|
|||
ilg.Emit(OpCodes.Dup); // signals
|
|||
ilg.Emit(OpCodes.Ldc_I4, idx); // i
|
|||
|
|||
{ |
|||
// Watch(...)
|
|||
{ |
|||
// (IDbusInterface)this
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldfld, s_objectField); |
|||
ilg.Emit(OpCodes.Castclass, dbusInterface.Type); |
|||
} |
|||
|
|||
{ |
|||
//SendSignalAction
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldftn, GenSendSignal(signal, signal == dbusInterface.PropertiesChangedSignal)); |
|||
ilg.Emit(OpCodes.Newobj, signal.ActionType.GetConstructors()[0]); |
|||
} |
|||
|
|||
if (signal.HasOnError) |
|||
{ |
|||
// Action<Exception> = null
|
|||
ilg.Emit(OpCodes.Ldnull); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Callvirt, signal.MethodInfo); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Stelem_Ref); |
|||
|
|||
idx++; |
|||
} |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
} |
|||
|
|||
private void ImplementConstructor(TypeDescription typeDescription) |
|||
{ |
|||
var dbusInterfaces = typeDescription.Interfaces; |
|||
// DBusConnection connection, ObjectPath objectPath, object o, IProxyFactory factory
|
|||
var constructor = _typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, s_dbusAdaptorConstructorParameterTypes); |
|||
var ilg = constructor.GetILGenerator(); |
|||
|
|||
{ |
|||
// base constructor
|
|||
ilg.Emit(OpCodes.Ldarg_0); // this
|
|||
ilg.Emit(OpCodes.Ldarg_1); // DBusConnection
|
|||
ilg.Emit(OpCodes.Ldarg_2); // ObjectPath
|
|||
ilg.Emit(OpCodes.Ldarg_3); // object
|
|||
ilg.Emit(OpCodes.Ldarg, 4); // IProxyFactory
|
|||
ilg.Emit(OpCodes.Ldarg, 5); // SynchronizationContext
|
|||
ilg.Emit(OpCodes.Call, s_baseConstructor); |
|||
} |
|||
|
|||
var introspectionXml = GenerateIntrospectionXml(typeDescription); |
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldstr, introspectionXml); |
|||
ilg.Emit(OpCodes.Stfld, s_setTypeIntrospectionField); |
|||
|
|||
foreach (var dbusInterface in dbusInterfaces) |
|||
{ |
|||
IEnumerable<MethodDescription> methods = dbusInterface.Methods ?? Array.Empty<MethodDescription>(); |
|||
|
|||
var propertyMethods = new[] { dbusInterface.GetPropertyMethod, |
|||
dbusInterface.GetAllPropertiesMethod, |
|||
dbusInterface.SetPropertyMethod }; |
|||
|
|||
methods = methods.Concat(propertyMethods); |
|||
|
|||
foreach (var method in methods) |
|||
{ |
|||
if (method == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (method.IsGenericOut) |
|||
{ |
|||
throw new NotImplementedException($"Cannot adaptor class for generic method {method.MethodInfo.ToString()}. Refactor the method to return Task<object>."); |
|||
} |
|||
|
|||
var signature = method.InSignature; |
|||
var memberName = method.Name; |
|||
bool isPropertyMethod = propertyMethods.Contains(method); |
|||
string key = isPropertyMethod ? DBusAdapter.GetPropertyAddKey(dbusInterface.Name, memberName, signature) : |
|||
DBusAdapter.GetMethodLookupKey(dbusInterface.Name, memberName, signature); |
|||
// _methodHandlers.Add(key, GenMethodHandler)
|
|||
{ |
|||
// _methodHandlers
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldfld, s_methodDictionaryField); |
|||
|
|||
// key
|
|||
ilg.Emit(OpCodes.Ldstr, key); |
|||
|
|||
// value
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldftn, GenMethodHandler(key, method, isPropertyMethod)); |
|||
ilg.Emit(OpCodes.Newobj, s_methodHandlerConstructor); |
|||
|
|||
// Add
|
|||
ilg.Emit(OpCodes.Call, s_methodDictionaryAdd); |
|||
} |
|||
} |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
} |
|||
|
|||
private MethodInfo GenSendSignal(SignalDescription signalDescription, bool isPropertiesChangedSignal) |
|||
{ |
|||
var key = $"{signalDescription.Interface.Name}.{signalDescription.Name}"; |
|||
var method = _typeBuilder.DefineMethod($"Emit{key}".Replace('.', '_'), MethodAttributes.Private, null, |
|||
signalDescription.SignalType == null ? Type.EmptyTypes : new[] { signalDescription.SignalType }); |
|||
|
|||
var ilg = method.GetILGenerator(); |
|||
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
if (isPropertiesChangedSignal) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, "org.freedesktop.DBus.Properties"); |
|||
ilg.Emit(OpCodes.Ldstr, "PropertiesChanged"); |
|||
} |
|||
else |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.Interface.Name); |
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.Name); |
|||
} |
|||
|
|||
if (signalDescription.SignalType == null) |
|||
{ |
|||
ilg.Emit(OpCodes.Call, s_emitVoidSignal); |
|||
} |
|||
else |
|||
{ |
|||
// Signature
|
|||
if (isPropertiesChangedSignal) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, "sa{sv}as"); |
|||
ilg.Emit(OpCodes.Newobj, s_signatureConstructor); |
|||
ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor); |
|||
} |
|||
else if (signalDescription.SignalSignature.HasValue) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.SignalSignature.Value.Value); |
|||
ilg.Emit(OpCodes.Newobj, s_signatureConstructor); |
|||
ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor); |
|||
} |
|||
else |
|||
{ |
|||
LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloca_S, signature); |
|||
ilg.Emit(OpCodes.Initobj, s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloc, signature); |
|||
} |
|||
|
|||
// Writer
|
|||
ilg.Emit(OpCodes.Newobj, s_messageWriterConstructor); |
|||
|
|||
if (isPropertiesChangedSignal) |
|||
{ |
|||
ilg.Emit(OpCodes.Dup); |
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.Interface.Name); |
|||
ilg.Emit(OpCodes.Call, s_writerWriteString); |
|||
ilg.Emit(OpCodes.Dup); |
|||
ilg.Emit(OpCodes.Call, s_writerSetSkipNextStructPadding); |
|||
} |
|||
ilg.Emit(OpCodes.Dup); |
|||
ilg.Emit(OpCodes.Ldarg_1); |
|||
ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(signalDescription.SignalType, isCompileTimeType: true)); |
|||
|
|||
ilg.Emit(OpCodes.Call, s_emitNonVoidSignal); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
|
|||
return method; |
|||
} |
|||
|
|||
private MethodInfo GenMethodHandler(string key, MethodDescription dbusMethod, bool propertyMethod) |
|||
{ |
|||
// Task<Message> MethodCall(object o(Ldarg_1), Message methodCall(Ldarg_2), IProxyFactory(Ldarg_3));
|
|||
|
|||
string methodName = $"Handle{key}".Replace('.', '_'); |
|||
var method = _typeBuilder.DefineMethod(methodName, MethodAttributes.Private, s_taskOfMessageType, s_methodHandlerParameterTypes); |
|||
|
|||
var ilg = method.GetILGenerator(); |
|||
|
|||
// call CreateReply
|
|||
|
|||
// this
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
// Message
|
|||
ilg.Emit(OpCodes.Ldarg_2); |
|||
// Task = (IDbusInterface)object.CallMethod(arguments)
|
|||
{ |
|||
// (IIinterface)object
|
|||
ilg.Emit(OpCodes.Ldarg_1); |
|||
ilg.Emit(OpCodes.Castclass, dbusMethod.Interface.Type); |
|||
|
|||
// Arguments
|
|||
if (dbusMethod.InArguments.Count != 0) |
|||
{ |
|||
// create reader for reading the arguments
|
|||
ilg.Emit(OpCodes.Ldarg_2); // message
|
|||
ilg.Emit(OpCodes.Ldarg_3); // IProxyFactory
|
|||
LocalBuilder reader = ilg.DeclareLocal(s_messageReaderType); |
|||
ilg.Emit(OpCodes.Newobj, s_messageReaderConstructor); // new MessageReader(message, proxyFactory)
|
|||
ilg.Emit(OpCodes.Stloc, reader); |
|||
|
|||
if (propertyMethod) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldloc, reader); |
|||
ilg.Emit(OpCodes.Call, s_readerSkipString); |
|||
} |
|||
|
|||
foreach (var argument in dbusMethod.InArguments) |
|||
{ |
|||
Type parameterType = argument.Type; |
|||
ilg.Emit(OpCodes.Ldloc, reader); |
|||
ilg.Emit(OpCodes.Call, ReadMethodFactory.CreateReadMethodForType(parameterType)); |
|||
} |
|||
} |
|||
|
|||
// Call method
|
|||
ilg.Emit(OpCodes.Callvirt, dbusMethod.MethodInfo); |
|||
} |
|||
|
|||
if (dbusMethod.OutType != null) |
|||
{ |
|||
// Action<MessageWriter, T>
|
|||
ilg.Emit(OpCodes.Ldnull); |
|||
ilg.Emit(OpCodes.Ldftn, WriteMethodFactory.CreateWriteMethodForType(dbusMethod.OutType, isCompileTimeType: true)); |
|||
var actionConstructor = s_action2GenericType.MakeGenericType(new[] { s_messageWriterType, dbusMethod.OutType }).GetConstructors()[0]; |
|||
ilg.Emit(OpCodes.Newobj, actionConstructor); |
|||
|
|||
// signature
|
|||
if (dbusMethod.OutSignature.HasValue) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, dbusMethod.OutSignature.Value.Value); |
|||
ilg.Emit(OpCodes.Newobj, s_signatureConstructor); |
|||
ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor); |
|||
} |
|||
else |
|||
{ |
|||
LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloca_S, signature); |
|||
ilg.Emit(OpCodes.Initobj, s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloc, signature); |
|||
} |
|||
|
|||
// CreateReply
|
|||
ilg.Emit(OpCodes.Call, s_createNonVoidReply.MakeGenericMethod(new[] { dbusMethod.OutType })); |
|||
} |
|||
else |
|||
{ |
|||
// CreateReply
|
|||
ilg.Emit(OpCodes.Call, s_createVoidReply); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
|
|||
return method; |
|||
} |
|||
|
|||
private string GenerateIntrospectionXml(TypeDescription description) |
|||
{ |
|||
var writer = new IntrospectionWriter(); |
|||
bool hasProperties = false; |
|||
|
|||
foreach (var interf in description.Interfaces) |
|||
{ |
|||
writer.WriteInterfaceStart(interf.Name); |
|||
foreach (var method in interf.Methods) |
|||
{ |
|||
writer.WriteMethodStart(method.Name); |
|||
foreach (var arg in method.InArguments) |
|||
{ |
|||
writer.WriteInArg(arg.Name, arg.Signature); |
|||
} |
|||
foreach (var arg in method.OutArguments) |
|||
{ |
|||
writer.WriteOutArg(arg.Name, arg.Signature); |
|||
} |
|||
writer.WriteMethodEnd(); |
|||
} |
|||
|
|||
foreach (var signal in interf.Signals) |
|||
{ |
|||
writer.WriteSignalStart(signal.Name); |
|||
foreach (var arg in signal.SignalArguments) |
|||
{ |
|||
writer.WriteArg(arg.Name, arg.Signature); |
|||
} |
|||
writer.WriteSignalEnd(); |
|||
} |
|||
|
|||
foreach (var prop in interf.Properties) |
|||
{ |
|||
hasProperties = true; |
|||
writer.WriteProperty(prop.Name, prop.Signature, prop.Access); |
|||
} |
|||
writer.WriteInterfaceEnd(); |
|||
} |
|||
if (hasProperties) |
|||
{ |
|||
writer.WritePropertiesInterface(); |
|||
} |
|||
writer.WriteIntrospectableInterface(); |
|||
writer.WritePeerInterface(); |
|||
|
|||
return writer.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,145 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class DBusObjectProxy : IDBusObject |
|||
{ |
|||
private readonly Connection2 _connection2; |
|||
private readonly IProxyFactory _factory; |
|||
public readonly string _serviceName; |
|||
|
|||
protected DBusObjectProxy(Connection2 connection2, IProxyFactory factory, string serviceName, ObjectPath2 objectPath2) |
|||
{ |
|||
_connection2 = connection2; |
|||
_serviceName = serviceName; |
|||
ObjectPath2 = objectPath2; |
|||
_factory = factory; |
|||
} |
|||
public ObjectPath2 ObjectPath2 { get; } |
|||
|
|||
internal protected async Task<IDisposable> WatchNonVoidSignalAsync<T>(string iface, string member, Action<Exception> error, Action<T> action, ReadMethodDelegate<T> readValue, bool isPropertiesChanged) |
|||
{ |
|||
var synchronizationContext = _connection2.CaptureSynchronizationContext(); |
|||
var wrappedDisposable = new WrappedDisposable(synchronizationContext); |
|||
SignalHandler handler = (msg, ex) => |
|||
{ |
|||
if (ex != null) |
|||
{ |
|||
if (error == null) |
|||
{ |
|||
return; |
|||
} |
|||
wrappedDisposable.Call(error, ex, disposes: true); |
|||
return; |
|||
} |
|||
|
|||
if (!SenderMatches(msg)) |
|||
{ |
|||
return; |
|||
} |
|||
var reader = new MessageReader(msg, _factory); |
|||
if (isPropertiesChanged) |
|||
{ |
|||
var eventIface = reader.ReadString(); |
|||
if (eventIface != iface) |
|||
{ |
|||
return; |
|||
} |
|||
reader.SetSkipNextStructPadding(); |
|||
} |
|||
var value = readValue(reader); |
|||
wrappedDisposable.Call(action, value); |
|||
}; |
|||
|
|||
if (isPropertiesChanged) |
|||
{ |
|||
wrappedDisposable.Disposable = await _connection2.WatchSignalAsync(ObjectPath2, "org.freedesktop.DBus.Properties", "PropertiesChanged", handler).ConfigureAwait(false); |
|||
} |
|||
else |
|||
{ |
|||
wrappedDisposable.Disposable = await _connection2.WatchSignalAsync(ObjectPath2, iface, member, handler).ConfigureAwait(false); |
|||
} |
|||
|
|||
return wrappedDisposable; |
|||
} |
|||
|
|||
internal protected async Task<IDisposable> WatchVoidSignalAsync(string iface, string member, Action<Exception> error, Action action) |
|||
{ |
|||
var synchronizationContext = _connection2.CaptureSynchronizationContext(); |
|||
var wrappedDisposable = new WrappedDisposable(synchronizationContext); |
|||
SignalHandler handler = (msg, ex) => |
|||
{ |
|||
if (ex != null) |
|||
{ |
|||
if (error == null) |
|||
{ |
|||
return; |
|||
} |
|||
wrappedDisposable.Call(error, ex, disposes: true); |
|||
return; |
|||
} |
|||
|
|||
if (!SenderMatches(msg)) |
|||
{ |
|||
return; |
|||
} |
|||
wrappedDisposable.Call(action); |
|||
}; |
|||
|
|||
wrappedDisposable.Disposable = await _connection2.WatchSignalAsync(ObjectPath2, iface, member, handler).ConfigureAwait(false); |
|||
|
|||
return wrappedDisposable; |
|||
} |
|||
|
|||
internal protected async Task<T> CallNonVoidMethodAsync<T>(string iface, string member, Signature? inSignature, MessageWriter writer, ReadMethodDelegate<T> readValue) |
|||
{ |
|||
var reader = await SendMethodReturnReaderAsync(iface, member, inSignature, writer).ConfigureAwait(false); |
|||
return readValue(reader); |
|||
} |
|||
|
|||
internal protected async Task<T> CallGenericOutMethodAsync<T>(string iface, string member, Signature? inSignature, MessageWriter writer) |
|||
{ |
|||
var reader = await SendMethodReturnReaderAsync(iface, member, inSignature, writer).ConfigureAwait(false); |
|||
return reader.ReadVariantAsType<T>(); |
|||
} |
|||
|
|||
internal protected Task CallVoidMethodAsync(string iface, string member, Signature? inSigStr, MessageWriter writer) |
|||
{ |
|||
return SendMethodReturnReaderAsync(iface, member, inSigStr, writer); |
|||
} |
|||
|
|||
private async Task<MessageReader> SendMethodReturnReaderAsync(string iface, string member, Signature? inSignature, MessageWriter writer) |
|||
{ |
|||
var callMessage = new Message( |
|||
new Header(MessageType.MethodCall) |
|||
{ |
|||
Path = ObjectPath2, |
|||
Interface = iface, |
|||
Member = member, |
|||
Destination = _serviceName, |
|||
Signature = inSignature |
|||
}, |
|||
writer?.ToArray(), |
|||
writer?.UnixFds |
|||
); |
|||
|
|||
var reply = await _connection2.CallMethodAsync(callMessage).ConfigureAwait(false); |
|||
return new MessageReader(reply, _factory); |
|||
} |
|||
|
|||
private bool SenderMatches(Message message) |
|||
{ |
|||
return string.IsNullOrEmpty(message.Header.Sender) || |
|||
string.IsNullOrEmpty(_serviceName) || |
|||
(_serviceName[0] != ':' && message.Header.Sender[0] == ':') || |
|||
_serviceName == message.Header.Sender; |
|||
} |
|||
} |
|||
} |
|||
@ -1,276 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Reflection.Emit; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class DBusObjectProxyTypeBuilder |
|||
{ |
|||
private static readonly ConstructorInfo s_messageWriterConstructor = typeof(MessageWriter).GetConstructor(Type.EmptyTypes); |
|||
private static readonly Type[] s_dbusObjectProxyConstructorParameterTypes = new Type[] { typeof(Connection2), typeof(IProxyFactory), typeof(string), typeof(ObjectPath2) }; |
|||
private static readonly ConstructorInfo s_baseConstructor = typeof(DBusObjectProxy).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, s_dbusObjectProxyConstructorParameterTypes); |
|||
private static readonly ConstructorInfo s_signatureConstructor = typeof(Signature).GetConstructor(new Type[] { typeof(string) }); |
|||
private static readonly ConstructorInfo s_nullableSignatureConstructor = typeof(Signature?).GetConstructor(new Type[] { typeof(Signature) }); |
|||
private static readonly MethodInfo s_callNonVoidMethod = typeof(DBusObjectProxy).GetMethod(nameof(DBusObjectProxy.CallNonVoidMethodAsync), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_callGenericOutMethod = typeof(DBusObjectProxy).GetMethod(nameof(DBusObjectProxy.CallGenericOutMethodAsync), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_callVoidMethod = typeof(DBusObjectProxy).GetMethod(nameof(DBusObjectProxy.CallVoidMethodAsync), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_watchNonVoidSignal = typeof(DBusObjectProxy).GetMethod(nameof(DBusObjectProxy.WatchNonVoidSignalAsync), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly MethodInfo s_watchVoidSignal = typeof(DBusObjectProxy).GetMethod(nameof(DBusObjectProxy.WatchVoidSignalAsync), BindingFlags.Instance | BindingFlags.NonPublic); |
|||
private static readonly Type s_nullableSignatureType = typeof(Signature?); |
|||
private static readonly Type s_dbusObjectProxyType = typeof(DBusObjectProxy); |
|||
private static readonly Type s_messageWriterType = typeof(MessageWriter); |
|||
private static readonly Type s_readMethodDelegateGenericType = typeof(ReadMethodDelegate<>); |
|||
|
|||
private readonly ModuleBuilder _moduleBuilder; |
|||
private TypeBuilder _typeBuilder; |
|||
|
|||
public DBusObjectProxyTypeBuilder(ModuleBuilder module) |
|||
{ |
|||
_moduleBuilder = module; |
|||
} |
|||
|
|||
public TypeInfo Build(Type interfaceType) |
|||
{ |
|||
if (_typeBuilder != null) |
|||
{ |
|||
throw new InvalidOperationException("Type has already been built."); |
|||
} |
|||
|
|||
var proxyName = interfaceType.FullName + "Proxy"; |
|||
_typeBuilder = _moduleBuilder.DefineType(proxyName, TypeAttributes.Class | TypeAttributes.Public, s_dbusObjectProxyType); |
|||
|
|||
var description = TypeDescription.DescribeInterface(interfaceType); |
|||
|
|||
ImplementConstructor(); |
|||
|
|||
if (!description.Interfaces.Any(dbusInterface => dbusInterface.Type == interfaceType)) |
|||
{ |
|||
_typeBuilder.AddInterfaceImplementation(interfaceType); |
|||
} |
|||
|
|||
foreach (var dbusInterface in description.Interfaces) |
|||
{ |
|||
ImplementDBusInterface(dbusInterface); |
|||
} |
|||
|
|||
return _typeBuilder.CreateTypeInfo(); |
|||
} |
|||
|
|||
private void ImplementConstructor() |
|||
{ |
|||
//DBusConnection connection, IProxyFactory factory, string serviceName, ObjectPath objectPath
|
|||
var constructor = _typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, s_dbusObjectProxyConstructorParameterTypes); |
|||
|
|||
ILGenerator ilg = constructor.GetILGenerator(); |
|||
|
|||
// base constructor
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Ldarg_1); |
|||
ilg.Emit(OpCodes.Ldarg_2); |
|||
ilg.Emit(OpCodes.Ldarg_3); |
|||
ilg.Emit(OpCodes.Ldarg, 4); |
|||
ilg.Emit(OpCodes.Call, s_baseConstructor); |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
} |
|||
|
|||
private void ImplementDBusInterface(InterfaceDescription dbusInterface) |
|||
{ |
|||
_typeBuilder.AddInterfaceImplementation(dbusInterface.Type); |
|||
|
|||
foreach (var method in dbusInterface.Methods) |
|||
{ |
|||
ImplementMethod(method, false); |
|||
} |
|||
|
|||
foreach (var method in new[] { dbusInterface.GetAllPropertiesMethod, |
|||
dbusInterface.GetPropertyMethod, |
|||
dbusInterface.SetPropertyMethod |
|||
}) |
|||
{ |
|||
if (method == null) |
|||
{ |
|||
continue; |
|||
} |
|||
ImplementMethod(method, true); |
|||
} |
|||
|
|||
foreach (var signal in dbusInterface.Signals) |
|||
{ |
|||
ImplementSignal(signal, false); |
|||
} |
|||
|
|||
if (dbusInterface.PropertiesChangedSignal != null) |
|||
{ |
|||
ImplementSignal(dbusInterface.PropertiesChangedSignal, true); |
|||
} |
|||
} |
|||
|
|||
private void ImplementSignal(SignalDescription signalDescription, bool isPropertiesChanged) |
|||
{ |
|||
var method = _typeBuilder.ImplementInterfaceMethod(signalDescription.MethodInfo); |
|||
ILGenerator ilg = method.GetILGenerator(); |
|||
|
|||
// call Watch(...)
|
|||
|
|||
// BusObject (this)
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Castclass, s_dbusObjectProxyType); |
|||
|
|||
// Interface
|
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.Interface.Name); |
|||
|
|||
// Member
|
|||
ilg.Emit(OpCodes.Ldstr, signalDescription.Name); |
|||
|
|||
// Action<Exception>
|
|||
if (signalDescription.HasOnError) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldarg_2); |
|||
} |
|||
else |
|||
{ |
|||
ilg.Emit(OpCodes.Ldnull); |
|||
} |
|||
|
|||
// Action/Action<>
|
|||
ilg.Emit(OpCodes.Ldarg_1); |
|||
|
|||
if (signalDescription.SignalType != null) |
|||
{ |
|||
// ReadMethodDelegate
|
|||
ilg.Emit(OpCodes.Ldnull); |
|||
ilg.Emit(OpCodes.Ldftn, ReadMethodFactory.CreateReadMethodForType(signalDescription.SignalType)); |
|||
var readDelegateConstructor = s_readMethodDelegateGenericType.MakeGenericType(new[] { signalDescription.SignalType }).GetConstructors()[0]; |
|||
ilg.Emit(OpCodes.Newobj, readDelegateConstructor); |
|||
|
|||
// isPropertiesChanged
|
|||
ilg.Emit(isPropertiesChanged ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); |
|||
|
|||
// Watch
|
|||
ilg.Emit(OpCodes.Call, s_watchNonVoidSignal.MakeGenericMethod(new[] { signalDescription.SignalType })); |
|||
} |
|||
else |
|||
{ |
|||
// Watch
|
|||
ilg.Emit(OpCodes.Call, s_watchVoidSignal); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
} |
|||
|
|||
private void ImplementMethod(MethodDescription methodDescription, bool propertyMethod) |
|||
{ |
|||
var method = _typeBuilder.ImplementInterfaceMethod(methodDescription.MethodInfo); |
|||
|
|||
ILGenerator ilg = method.GetILGenerator(); |
|||
|
|||
//CallMethod(...)
|
|||
|
|||
// BusObject (this)
|
|||
ilg.Emit(OpCodes.Ldarg_0); |
|||
ilg.Emit(OpCodes.Castclass, s_dbusObjectProxyType); |
|||
|
|||
// Interface
|
|||
if (propertyMethod) |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, "org.freedesktop.DBus.Properties"); |
|||
} |
|||
else |
|||
{ |
|||
ilg.Emit(OpCodes.Ldstr, methodDescription.Interface.Name); |
|||
} |
|||
|
|||
// Member
|
|||
ilg.Emit(OpCodes.Ldstr, methodDescription.Name); |
|||
|
|||
// Signature
|
|||
if (methodDescription.InSignature.HasValue || propertyMethod) |
|||
{ |
|||
string inSig = methodDescription.InSignature?.Value ?? string.Empty; |
|||
if (propertyMethod) |
|||
{ |
|||
inSig = "s" + inSig; |
|||
} |
|||
ilg.Emit(OpCodes.Ldstr, inSig); |
|||
ilg.Emit(OpCodes.Newobj, s_signatureConstructor); |
|||
ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor); |
|||
} |
|||
else |
|||
{ |
|||
LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloca_S, signature); |
|||
ilg.Emit(OpCodes.Initobj, s_nullableSignatureType); |
|||
ilg.Emit(OpCodes.Ldloc, signature); |
|||
} |
|||
|
|||
// MessageWriter
|
|||
var argumentOffset = 1; //offset by one to account for "this"
|
|||
if (methodDescription.InArguments.Count != 0 || propertyMethod) |
|||
{ |
|||
LocalBuilder writer = ilg.DeclareLocal(s_messageWriterType); |
|||
ilg.Emit(OpCodes.Newobj, s_messageWriterConstructor); |
|||
ilg.Emit(OpCodes.Stloc, writer); |
|||
|
|||
if (propertyMethod) |
|||
{ |
|||
// Write parameter
|
|||
Type parameterType = typeof(string); |
|||
ilg.Emit(OpCodes.Ldloc, writer); |
|||
ilg.Emit(OpCodes.Ldstr, methodDescription.Interface.Name); |
|||
ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(parameterType, isCompileTimeType: true)); |
|||
} |
|||
|
|||
foreach (var argument in methodDescription.InArguments) |
|||
{ |
|||
// Write parameter
|
|||
Type parameterType = argument.Type; |
|||
ilg.Emit(OpCodes.Ldloc, writer); |
|||
ilg.Emit(OpCodes.Ldarg, argumentOffset); |
|||
ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(parameterType, isCompileTimeType: true)); |
|||
|
|||
argumentOffset++; |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ldloc, writer); |
|||
} |
|||
else |
|||
{ |
|||
ilg.Emit(OpCodes.Ldnull); |
|||
} |
|||
|
|||
if (methodDescription.OutType != null) |
|||
{ |
|||
// CallMethod
|
|||
if (methodDescription.IsGenericOut) |
|||
{ |
|||
Type genericParameter = method.GetGenericArguments()[0]; |
|||
ilg.Emit(OpCodes.Call, s_callGenericOutMethod.MakeGenericMethod(new[] { genericParameter })); |
|||
} |
|||
else |
|||
{ |
|||
// ReadMethodDelegate
|
|||
ilg.Emit(OpCodes.Ldnull); |
|||
ilg.Emit(OpCodes.Ldftn, ReadMethodFactory.CreateReadMethodForType(methodDescription.OutType)); |
|||
var readDelegateConstructor = s_readMethodDelegateGenericType.MakeGenericType(new[] { methodDescription.OutType }).GetConstructors()[0]; |
|||
ilg.Emit(OpCodes.Newobj, readDelegateConstructor); |
|||
|
|||
ilg.Emit(OpCodes.Call, s_callNonVoidMethod.MakeGenericMethod(new[] { methodDescription.OutType })); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// CallMethod
|
|||
ilg.Emit(OpCodes.Call, s_callVoidMethod); |
|||
} |
|||
|
|||
ilg.Emit(OpCodes.Ret); |
|||
} |
|||
} |
|||
} |
|||
@ -1,115 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
using System.Reflection.Emit; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
[assembly: InternalsVisibleTo(Tmds.DBus.Connection2.DynamicAssemblyName)] |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class DynamicAssembly |
|||
{ |
|||
public static readonly DynamicAssembly Instance = new DynamicAssembly(); |
|||
|
|||
private readonly AssemblyBuilder _assemblyBuilder; |
|||
private readonly ModuleBuilder _moduleBuilder; |
|||
private readonly Dictionary<Type, TypeInfo> _proxyTypeMap; |
|||
private readonly Dictionary<Type, TypeInfo> _adapterTypeMap; |
|||
private readonly object _gate = new object(); |
|||
|
|||
private DynamicAssembly() |
|||
{ |
|||
byte[] keyBuffer; |
|||
using (Stream keyStream = typeof(DynamicAssembly).GetTypeInfo().Assembly.GetManifestResourceStream("Tmds.DBus.sign.snk")) |
|||
{ |
|||
if (keyStream == null) |
|||
{ |
|||
throw new InvalidOperationException("'Tmds.DBus.sign.snk' not found in resources"); |
|||
} |
|||
keyBuffer = new byte[keyStream.Length]; |
|||
keyStream.Read(keyBuffer, 0, keyBuffer.Length); |
|||
} |
|||
|
|||
var dynamicAssemblyName = "Tmds.DBus.Emit"; |
|||
var assemblyName = new AssemblyName(dynamicAssemblyName); |
|||
assemblyName.Version = new Version(1, 0, 0); |
|||
assemblyName.Flags = AssemblyNameFlags.PublicKey; |
|||
assemblyName.SetPublicKey(keyBuffer); |
|||
_assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); |
|||
_moduleBuilder = _assemblyBuilder.DefineDynamicModule(dynamicAssemblyName); |
|||
_proxyTypeMap = new Dictionary<Type, TypeInfo>(); |
|||
_adapterTypeMap = new Dictionary<Type, TypeInfo>(); |
|||
} |
|||
|
|||
public TypeInfo GetProxyTypeInfo(Type interfaceType) |
|||
{ |
|||
TypeInfo typeInfo; |
|||
lock (_proxyTypeMap) |
|||
{ |
|||
if (_proxyTypeMap.TryGetValue(interfaceType, out typeInfo)) |
|||
{ |
|||
return typeInfo; |
|||
} |
|||
} |
|||
|
|||
lock (_gate) |
|||
{ |
|||
lock (_proxyTypeMap) |
|||
{ |
|||
if (_proxyTypeMap.TryGetValue(interfaceType, out typeInfo)) |
|||
{ |
|||
return typeInfo; |
|||
} |
|||
} |
|||
|
|||
typeInfo = new DBusObjectProxyTypeBuilder(_moduleBuilder).Build(interfaceType); |
|||
|
|||
lock (_proxyTypeMap) |
|||
{ |
|||
_proxyTypeMap[interfaceType] = typeInfo; |
|||
} |
|||
|
|||
return typeInfo; |
|||
} |
|||
} |
|||
|
|||
public TypeInfo GetExportTypeInfo(Type objectType) |
|||
{ |
|||
TypeInfo typeInfo; |
|||
|
|||
lock (_adapterTypeMap) |
|||
{ |
|||
if (_adapterTypeMap.TryGetValue(objectType, out typeInfo)) |
|||
{ |
|||
return typeInfo; |
|||
} |
|||
} |
|||
|
|||
lock (_gate) |
|||
{ |
|||
lock (_adapterTypeMap) |
|||
{ |
|||
if (_adapterTypeMap.TryGetValue(objectType, out typeInfo)) |
|||
{ |
|||
return typeInfo; |
|||
} |
|||
} |
|||
|
|||
typeInfo = new DBusAdapterTypeBuilder(_moduleBuilder).Build(objectType); |
|||
|
|||
lock (_adapterTypeMap) |
|||
{ |
|||
_adapterTypeMap[objectType] = typeInfo; |
|||
} |
|||
|
|||
return typeInfo; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,62 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class InterfaceDescription |
|||
{ |
|||
public InterfaceDescription(Type type, string name, IList<MethodDescription> methods, IList<SignalDescription> signals, |
|||
IList<PropertyDescription> properties, MethodDescription propertyGetMethod, MethodDescription propertyGetAllMethod, MethodDescription propertySetMethod, |
|||
SignalDescription propertiesChangedSignal) |
|||
{ |
|||
Type = type; |
|||
Name = name; |
|||
_methods = methods; |
|||
_signals = signals; |
|||
GetAllPropertiesMethod = propertyGetAllMethod; |
|||
SetPropertyMethod = propertySetMethod; |
|||
GetPropertyMethod = propertyGetMethod; |
|||
PropertiesChangedSignal = propertiesChangedSignal; |
|||
_properties = properties; |
|||
|
|||
foreach (var signal in Signals) |
|||
{ |
|||
signal.Interface = this; |
|||
} |
|||
if (propertiesChangedSignal != null) |
|||
{ |
|||
PropertiesChangedSignal.Interface = this; |
|||
} |
|||
foreach (var method in Methods) |
|||
{ |
|||
method.Interface = this; |
|||
} |
|||
foreach (var method in new[] { GetPropertyMethod, |
|||
SetPropertyMethod, |
|||
GetAllPropertiesMethod}) |
|||
{ |
|||
if (method != null) |
|||
{ |
|||
method.Interface = this; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Type Type { get; } |
|||
public string Name { get; } |
|||
private IList<MethodDescription> _methods; |
|||
public IList<MethodDescription> Methods { get { return _methods ?? Array.Empty<MethodDescription>(); } } |
|||
private IList<SignalDescription> _signals; |
|||
public IList<SignalDescription> Signals { get { return _signals ?? Array.Empty<SignalDescription>(); } } |
|||
public MethodDescription GetPropertyMethod { get; } |
|||
public MethodDescription GetAllPropertiesMethod { get; } |
|||
public MethodDescription SetPropertyMethod { get; } |
|||
public SignalDescription PropertiesChangedSignal { get; } |
|||
private IList<PropertyDescription> _properties; |
|||
public IList<PropertyDescription> Properties { get { return _properties ?? Array.Empty<PropertyDescription>(); } } |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class MethodDescription |
|||
{ |
|||
public MethodDescription(MethodInfo member, string name, IList<ArgumentDescription> inArguments, Signature? inSignature, Type outType, bool isGenericOut, Signature? outSignature, IList<ArgumentDescription> outArguments) |
|||
{ |
|||
MethodInfo = member; |
|||
Name = name; |
|||
_inArguments = inArguments; |
|||
InSignature = inSignature; |
|||
OutType = outType; |
|||
IsGenericOut = isGenericOut; |
|||
OutSignature = outSignature; |
|||
_outArguments = outArguments; |
|||
} |
|||
|
|||
public InterfaceDescription Interface { get; internal set; } |
|||
public MethodInfo MethodInfo { get; } |
|||
public string Name { get; } |
|||
public Type OutType { get; } |
|||
public Signature? OutSignature { get; } |
|||
private IList<ArgumentDescription> _outArguments; |
|||
public IList<ArgumentDescription> OutArguments { get { return _outArguments ?? Array.Empty<ArgumentDescription>(); } } |
|||
private IList<ArgumentDescription> _inArguments; |
|||
public IList<ArgumentDescription> InArguments { get { return _inArguments ?? Array.Empty<ArgumentDescription>(); } } |
|||
public Signature? InSignature { get; } |
|||
public bool IsGenericOut { get; } |
|||
|
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class PropertyDescription |
|||
{ |
|||
public PropertyDescription(string name, Signature type, PropertyAccess access) |
|||
{ |
|||
Name = name; |
|||
Signature = type; |
|||
Access = access; |
|||
} |
|||
|
|||
public string Name { get; } |
|||
public Signature Signature { get; } |
|||
public PropertyAccess Access { get; } |
|||
|
|||
} |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class PropertyTypeInspector |
|||
{ |
|||
public static void InspectField(FieldInfo field, out string propertyName, out Type propertyType) |
|||
{ |
|||
PropertyAttribute attribute = field.GetCustomAttribute<PropertyAttribute>(); |
|||
|
|||
if (attribute?.Name != null) |
|||
{ |
|||
propertyName = attribute.Name; |
|||
} |
|||
else |
|||
{ |
|||
propertyName = field.Name; |
|||
if (propertyName.StartsWith("_", StringComparison.Ordinal)) |
|||
{ |
|||
propertyName = propertyName.Substring(1); |
|||
} |
|||
} |
|||
|
|||
propertyType = field.FieldType; |
|||
var typeInfo = propertyType.GetTypeInfo(); |
|||
bool isNullableType = typeInfo.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>); |
|||
if (isNullableType) |
|||
{ |
|||
propertyType = Nullable.GetUnderlyingType(propertyType); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,162 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal delegate T ReadMethodDelegate<out T>(MessageReader reader); |
|||
|
|||
internal class ReadMethodFactory |
|||
{ |
|||
public static ReadMethodDelegate<T> CreateReadMethodDelegate<T>() |
|||
{ |
|||
var type = typeof(T); |
|||
var readMethod = CreateReadMethodForType(type); |
|||
return (ReadMethodDelegate<T>)readMethod.CreateDelegate(typeof(ReadMethodDelegate<T>)); |
|||
} |
|||
|
|||
public static MethodInfo CreateReadMethodForType(Type type) // Type Read(MessageReader)
|
|||
{ |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
return s_messageReaderReadEnum.MakeGenericMethod(new[] { type }); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
return s_messageReaderReadBoolean; |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return s_messageReaderReadByte; |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return s_messageReaderReadDouble; |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return s_messageReaderReadInt16; |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return s_messageReaderReadInt32; |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return s_messageReaderReadInt64; |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
return s_messageReaderReadObjectPath; |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
return s_messageReaderReadSignature; |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
return s_messageReaderReadString; |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return s_messageReaderReadSingle; |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return s_messageReaderReadUInt16; |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return s_messageReaderReadUInt32; |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return s_messageReaderReadUInt64; |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
return s_messageReaderReadVariant; |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
return s_messageReaderReadBusObject; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType: true)) |
|||
{ |
|||
return s_messageReaderReadDBusInterface.MakeGenericMethod(new[] { type }); |
|||
} |
|||
|
|||
Type elementType; |
|||
var enumerableType = ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType: true); |
|||
if (enumerableType != ArgTypeInspector.EnumerableType.NotEnumerable) |
|||
{ |
|||
if (enumerableType == ArgTypeInspector.EnumerableType.GenericDictionary) |
|||
{ |
|||
TypeInfo elementTypeInfo = elementType.GetTypeInfo(); |
|||
Type keyType = elementTypeInfo.GenericTypeArguments[0]; |
|||
Type valueType = elementTypeInfo.GenericTypeArguments[1]; |
|||
return s_messageReaderReadDictionary.MakeGenericMethod(new[] { keyType, valueType }); |
|||
} |
|||
else if (enumerableType == ArgTypeInspector.EnumerableType.AttributeDictionary) |
|||
{ |
|||
return s_messageReaderReadDictionaryObject.MakeGenericMethod(new[] { type }); |
|||
} |
|||
else // Enumerable, EnumerableKeyValuePair
|
|||
{ |
|||
return s_messageReaderReadArray.MakeGenericMethod(new[] { elementType }); |
|||
} |
|||
} |
|||
|
|||
bool isValueTuple; |
|||
if (ArgTypeInspector.IsStructType(type, out isValueTuple)) |
|||
{ |
|||
if (isValueTuple) |
|||
{ |
|||
return s_messageReaderReadValueTupleStruct.MakeGenericMethod(type); |
|||
} |
|||
else |
|||
{ |
|||
return s_messageReaderReadStruct.MakeGenericMethod(type); |
|||
} |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsSafeHandleType(type)) |
|||
{ |
|||
return s_messageReaderReadSafeHandle.MakeGenericMethod(type); |
|||
} |
|||
|
|||
throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'"); |
|||
} |
|||
|
|||
private static readonly MethodInfo s_messageReaderReadEnum = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadEnum)); |
|||
private static readonly MethodInfo s_messageReaderReadBoolean = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadBoolean)); |
|||
private static readonly MethodInfo s_messageReaderReadByte = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadByte)); |
|||
private static readonly MethodInfo s_messageReaderReadDouble = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadDouble)); |
|||
private static readonly MethodInfo s_messageReaderReadInt16 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadInt16)); |
|||
private static readonly MethodInfo s_messageReaderReadInt32 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadInt32)); |
|||
private static readonly MethodInfo s_messageReaderReadInt64 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadInt64)); |
|||
private static readonly MethodInfo s_messageReaderReadObjectPath = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadObjectPath)); |
|||
private static readonly MethodInfo s_messageReaderReadSignature = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadSignature)); |
|||
private static readonly MethodInfo s_messageReaderReadSafeHandle = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadSafeHandle)); |
|||
private static readonly MethodInfo s_messageReaderReadSingle = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadSingle)); |
|||
private static readonly MethodInfo s_messageReaderReadString = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadString)); |
|||
private static readonly MethodInfo s_messageReaderReadUInt16 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadUInt16)); |
|||
private static readonly MethodInfo s_messageReaderReadUInt32 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadUInt32)); |
|||
private static readonly MethodInfo s_messageReaderReadUInt64 = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadUInt64)); |
|||
private static readonly MethodInfo s_messageReaderReadVariant = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadVariant)); |
|||
private static readonly MethodInfo s_messageReaderReadBusObject = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadBusObject)); |
|||
private static readonly MethodInfo s_messageReaderReadDictionary = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadDictionary), Type.EmptyTypes); |
|||
private static readonly MethodInfo s_messageReaderReadDictionaryObject = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadDictionaryObject), Type.EmptyTypes); |
|||
private static readonly MethodInfo s_messageReaderReadArray = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadArray), Type.EmptyTypes); |
|||
private static readonly MethodInfo s_messageReaderReadStruct = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadStruct), Type.EmptyTypes); |
|||
private static readonly MethodInfo s_messageReaderReadValueTupleStruct = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadValueTupleStruct), Type.EmptyTypes); |
|||
private static readonly MethodInfo s_messageReaderReadDBusInterface = typeof(MessageReader).GetMethod(nameof(MessageReader.ReadDBusInterface)); |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class SignalDescription |
|||
{ |
|||
public SignalDescription(MethodInfo method, string name, Type actionType, Type signalType, Signature? signature, IList<ArgumentDescription> arguments, bool hasOnError) |
|||
{ |
|||
MethodInfo = method; |
|||
Name = name; |
|||
ActionType = actionType; |
|||
SignalType = signalType; |
|||
SignalSignature = signature; |
|||
_signalArguments = arguments; |
|||
HasOnError = hasOnError; |
|||
} |
|||
|
|||
public InterfaceDescription Interface { get; internal set; } |
|||
public MethodInfo MethodInfo { get; } |
|||
public string Name { get; } |
|||
public Type SignalType { get; } |
|||
public Signature? SignalSignature { get; } |
|||
private IList<ArgumentDescription> _signalArguments; |
|||
public IList<ArgumentDescription> SignalArguments { get { return _signalArguments ?? Array.Empty<ArgumentDescription>(); } } |
|||
public Type ActionType { get; } |
|||
public bool HasOnError { get; } |
|||
} |
|||
} |
|||
@ -1,79 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Reflection.Emit; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal static class TypeBuilderExtensions |
|||
{ |
|||
private static readonly Type s_parameterTaskType = typeof(Task<>); |
|||
|
|||
public static MethodBuilder OverrideAbstractMethod(this TypeBuilder typeBuilder, MethodInfo declMethod) |
|||
{ |
|||
var attributes = declMethod.Attributes; |
|||
attributes ^= MethodAttributes.NewSlot; |
|||
attributes |= MethodAttributes.ReuseSlot; |
|||
attributes |= MethodAttributes.Final; |
|||
|
|||
return DefineMethod(typeBuilder, attributes, declMethod); |
|||
} |
|||
|
|||
public static MethodBuilder ImplementInterfaceMethod(this TypeBuilder typeBuilder, MethodInfo declMethod) |
|||
{ |
|||
var attributes = declMethod.Attributes; |
|||
attributes ^= MethodAttributes.Abstract; |
|||
attributes ^= MethodAttributes.NewSlot; |
|||
attributes |= MethodAttributes.Final; |
|||
|
|||
return DefineMethod(typeBuilder, attributes, declMethod); |
|||
} |
|||
|
|||
private static MethodBuilder DefineMethod(TypeBuilder typeBuilder, MethodAttributes attributes, MethodInfo declMethod) |
|||
{ |
|||
if (declMethod.IsGenericMethod) |
|||
{ |
|||
return DefineGenericMethod(typeBuilder, attributes, declMethod); |
|||
} |
|||
var declParameters = declMethod.GetParameters(); |
|||
|
|||
var defineParameters = new Type[declParameters.Length]; |
|||
for (var i = 0; i < declParameters.Length; i++) |
|||
defineParameters[i] = declParameters[i].ParameterType; |
|||
|
|||
var methodBuilder = typeBuilder.DefineMethod(declMethod.Name, attributes, declMethod.ReturnType, defineParameters); |
|||
typeBuilder.DefineMethodOverride(methodBuilder, declMethod); |
|||
|
|||
for (var i = 0; i < declParameters.Length; i++) |
|||
methodBuilder.DefineParameter(i + 1, declParameters[i].Attributes, declParameters[i].Name); |
|||
|
|||
return methodBuilder; |
|||
} |
|||
|
|||
private static MethodBuilder DefineGenericMethod(TypeBuilder typeBuilder, MethodAttributes attributes, MethodInfo declMethod) |
|||
{ |
|||
var declParameters = declMethod.GetParameters(); |
|||
|
|||
var defineParameters = new Type[declParameters.Length]; |
|||
for (var i = 0; i < declParameters.Length; i++) |
|||
defineParameters[i] = declParameters[i].ParameterType; |
|||
|
|||
var methodBuilder = typeBuilder.DefineMethod(declMethod.Name, attributes); //, declMethod.ReturnType, defineParameters);
|
|||
GenericTypeParameterBuilder[] typeParameters = methodBuilder.DefineGenericParameters(new[] { "T" }); |
|||
methodBuilder.SetParameters(defineParameters); |
|||
methodBuilder.SetReturnType(s_parameterTaskType.MakeGenericType(new Type[] { typeParameters[0] })); |
|||
|
|||
typeBuilder.DefineMethodOverride(methodBuilder, declMethod); |
|||
|
|||
for (var i = 0; i < declParameters.Length; i++) |
|||
methodBuilder.DefineParameter(i + 1, declParameters[i].Attributes, declParameters[i].Name); |
|||
|
|||
return methodBuilder; |
|||
} |
|||
} |
|||
} |
|||
@ -1,449 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal class TypeDescription |
|||
{ |
|||
private static readonly Type s_signalReturnType = typeof(Task<IDisposable>); |
|||
private static readonly Type s_idbusObjectType = typeof(IDBusObject); |
|||
private static readonly Type s_emptyActionType = typeof(Action); |
|||
private static readonly Type s_exceptionActionType = typeof(Action<Exception>); |
|||
private static readonly Type s_singleParameterActionType = typeof(Action<>); |
|||
private static readonly Type s_emptyTaskType = typeof(Task); |
|||
private static readonly Type s_parameterTaskType = typeof(Task<>); |
|||
private static readonly Type s_cancellationTokenType = typeof(CancellationToken); |
|||
private static readonly Type s_stringType = typeof(string); |
|||
private static readonly Type s_objectType = typeof(object); |
|||
private static readonly Signature s_propertiesChangedSignature = new Signature("a{sv}as"); |
|||
private static readonly Signature s_getAllOutSignature = new Signature("a{sv}"); |
|||
private static readonly Type[] s_mappedTypes = new[] { typeof(bool), typeof(byte), typeof(double), typeof(short), typeof(int), |
|||
typeof(long), typeof(ObjectPath2), typeof(Signature), typeof(float), typeof(string), typeof(ushort), typeof(uint), typeof(ulong), |
|||
typeof(object), typeof(IDBusObject)}; |
|||
|
|||
public Type Type { get; } |
|||
|
|||
private IList<InterfaceDescription> _interfaces; |
|||
public IList<InterfaceDescription> Interfaces { get { return _interfaces ?? Array.Empty<InterfaceDescription>(); } } |
|||
|
|||
public static TypeDescription DescribeObject(Type type) |
|||
{ |
|||
return Describe(type, isInterfaceType: false); |
|||
} |
|||
|
|||
public static TypeDescription DescribeInterface(Type type) |
|||
{ |
|||
return Describe(type, isInterfaceType : true); |
|||
} |
|||
|
|||
private TypeDescription(Type type, IList<InterfaceDescription> interfaces) |
|||
{ |
|||
Type = type; |
|||
_interfaces = interfaces; |
|||
} |
|||
|
|||
private static TypeDescription Describe(Type type, bool isInterfaceType) |
|||
{ |
|||
var interfaces = new List<InterfaceDescription>(); |
|||
var typeInfo = type.GetTypeInfo(); |
|||
if (typeInfo.IsInterface != isInterfaceType) |
|||
{ |
|||
if (isInterfaceType) |
|||
{ |
|||
throw new ArgumentException($"Type '{type.FullName}' must be an interface type"); |
|||
} |
|||
else |
|||
{ |
|||
throw new ArgumentException($"Type '{type.FullName}' cannot be an interface type"); |
|||
} |
|||
} |
|||
|
|||
|
|||
if ((type != s_idbusObjectType) |
|||
&& !typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType)) |
|||
{ |
|||
throw new ArgumentException($"Type {type.FullName} does not implement {typeof(IDBusObject).FullName}"); |
|||
} |
|||
|
|||
if (!isInterfaceType) |
|||
{ |
|||
var dbusInterfaces = from interf in typeInfo.ImplementedInterfaces |
|||
let interfAttribute = interf.GetTypeInfo().GetCustomAttribute<DBusInterfaceAttribute>(false) |
|||
where interfAttribute != null |
|||
select new { Type = interf, Attribute = interfAttribute }; |
|||
|
|||
foreach (var dbusInterf in dbusInterfaces) |
|||
{ |
|||
AddInterfaceDescription(dbusInterf.Type, dbusInterf.Attribute, interfaces); |
|||
} |
|||
} |
|||
else if (type == s_idbusObjectType) |
|||
{} |
|||
else |
|||
{ |
|||
var interfaceAttribute = typeInfo.GetCustomAttribute<DBusInterfaceAttribute>(false); |
|||
if (interfaceAttribute != null) |
|||
{ |
|||
AddInterfaceDescription(type, interfaceAttribute, interfaces); |
|||
} |
|||
else |
|||
{ |
|||
if (typeInfo.DeclaredMembers.Count() != 0) |
|||
{ |
|||
throw new ArgumentException($"DBus object type {type.FullName} cannot implement methods. It must inherit one or more DBus interface types."); |
|||
} |
|||
} |
|||
|
|||
var dbusInterfaces = from interf in typeInfo.ImplementedInterfaces |
|||
where interf != s_idbusObjectType |
|||
let interfAttribute = interf.GetTypeInfo().GetCustomAttribute<DBusInterfaceAttribute>(false) |
|||
select new { Type = interf, Attribute = interfAttribute }; |
|||
|
|||
foreach (var dbusInterf in dbusInterfaces) |
|||
{ |
|||
AddInterfaceDescription(dbusInterf.Type, dbusInterf.Attribute, interfaces); |
|||
} |
|||
|
|||
if (dbusInterfaces.Any(interf => interf.Attribute == null)) |
|||
{ |
|||
throw new ArgumentException($"DBus object type {type.FullName} inherits one or more interfaces which are not DBus interface types."); |
|||
} |
|||
|
|||
if ((interfaces.Count == 0) && (!typeInfo.ImplementedInterfaces.Contains(s_idbusObjectType))) |
|||
{ |
|||
throw new ArgumentException($"Type {type.FullName} does not inherit '{s_idbusObjectType.FullName}' or any interfaces with the {typeof(DBusInterfaceAttribute).FullName}."); |
|||
} |
|||
} |
|||
return new TypeDescription(type, interfaces); |
|||
} |
|||
|
|||
private static void AddInterfaceDescription(Type type, DBusInterfaceAttribute interfaceAttribute, List<InterfaceDescription> interfaces) |
|||
{ |
|||
if (interfaces.Any(interf => interf.Name == interfaceAttribute.Name)) |
|||
{ |
|||
throw new ArgumentException($"DBus interface {interfaceAttribute.Name} is inherited multiple times"); |
|||
} |
|||
|
|||
IList<MethodDescription> methods = null; |
|||
IList<SignalDescription> signals = null; |
|||
IList<PropertyDescription> properties = null; |
|||
MethodDescription propertyGetMethod = null; |
|||
MethodDescription propertySetMethod = null; |
|||
MethodDescription propertyGetAllMethod = null; |
|||
SignalDescription propertiesChangedSignal = null; |
|||
Type propertyType = interfaceAttribute.PropertyType; |
|||
Type elementType; |
|||
if (propertyType != null && ArgTypeInspector.InspectEnumerableType(propertyType, out elementType, isCompileTimeType: true) != ArgTypeInspector.EnumerableType.AttributeDictionary) |
|||
{ |
|||
throw new ArgumentException($"Property type '{propertyType.FullName}' does not have the '{typeof(DictionaryAttribute).FullName}' attribute"); |
|||
} |
|||
|
|||
foreach (var member in type.GetMethods()) |
|||
{ |
|||
string memberName = member.ToString(); |
|||
if (!member.Name.EndsWith("Async", StringComparison.Ordinal)) |
|||
{ |
|||
throw new ArgumentException($"{memberName} does not end with 'Async'"); |
|||
} |
|||
var isSignal = member.Name.StartsWith("Watch", StringComparison.Ordinal); |
|||
if (isSignal) |
|||
{ |
|||
if (member.ReturnType != s_signalReturnType) |
|||
{ |
|||
throw new ArgumentException($"Signal {memberName} does not return 'Task<IDisposable>'"); |
|||
} |
|||
|
|||
var name = member.Name.Substring(5, member.Name.Length - 10); |
|||
if (name.Length == 0) |
|||
{ |
|||
throw new ArgumentException($"Signal {memberName} has an empty name"); |
|||
} |
|||
|
|||
Signature? parameterSignature = null; |
|||
IList<ArgumentDescription> arguments = null; |
|||
var parameters = member.GetParameters(); |
|||
var actionParameter = parameters.Length > 0 ? parameters[0] : null; |
|||
Type parameterType = null; |
|||
bool validActionParameter = false; |
|||
if (actionParameter != null) |
|||
{ |
|||
if (actionParameter.ParameterType == s_exceptionActionType) |
|||
{ |
|||
// actionParameter is missing
|
|||
} |
|||
else if (actionParameter.ParameterType == s_emptyActionType) |
|||
{ |
|||
validActionParameter = true; |
|||
} |
|||
else if (actionParameter.ParameterType.GetTypeInfo().IsGenericType |
|||
&& actionParameter.ParameterType.GetGenericTypeDefinition() == s_singleParameterActionType) |
|||
{ |
|||
validActionParameter = true; |
|||
parameterType = actionParameter.ParameterType.GetGenericArguments()[0]; |
|||
InspectParameterType(parameterType, actionParameter, out parameterSignature, out arguments); |
|||
} |
|||
} |
|||
if (parameters.Length > 0 && parameters[parameters.Length - 1].ParameterType == s_cancellationTokenType) |
|||
{ |
|||
throw new NotSupportedException($"Signal {memberName} does not support cancellation. See https://github.com/tmds/Tmds.DBus/issues/15."); |
|||
} |
|||
var lastParameter = parameters.Length > 0 ? parameters[parameters.Length - 1] : null; |
|||
bool hasOnError = lastParameter?.ParameterType == s_exceptionActionType; |
|||
if (!validActionParameter || parameters.Length != 1 + (hasOnError ? 1 : 0)) |
|||
{ |
|||
throw new ArgumentException($"Signal {memberName} must accept an argument of Type 'Action'/'Action<>' and optional argument of Type 'Action<Exception>'"); |
|||
} |
|||
|
|||
var signal = new SignalDescription(member, name, actionParameter.ParameterType, parameterType, parameterSignature, arguments, hasOnError); |
|||
if (member.Name == interfaceAttribute.WatchPropertiesMethod) |
|||
{ |
|||
if (propertiesChangedSignal != null) |
|||
{ |
|||
throw new ArgumentException($"Multiple property changes signals are declared: {memberName}, {propertyGetMethod.MethodInfo.ToString()}"); |
|||
} |
|||
propertiesChangedSignal = signal; |
|||
if (propertiesChangedSignal.SignalSignature != s_propertiesChangedSignature) |
|||
{ |
|||
throw new ArgumentException($"PropertiesChanged signal {memberName} must accept an Action<T> where T is a struct with an IDictionary<string, object> and an string[] field"); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
signals = signals ?? new List<SignalDescription>(); |
|||
signals.Add(signal); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var name = member.Name.Substring(0, member.Name.Length - 5); |
|||
if (name.Length == 0) |
|||
{ |
|||
throw new ArgumentException($"DBus Method {memberName} has an empty name"); |
|||
} |
|||
|
|||
IList<ArgumentDescription> outArguments = null; |
|||
Signature? outSignature = null; |
|||
var taskParameter = member.ReturnType; |
|||
Type outType = null; |
|||
bool valid = false; |
|||
bool isGenericOut = false; |
|||
if (taskParameter != null) |
|||
{ |
|||
if (taskParameter == s_emptyTaskType) |
|||
{ |
|||
valid = true; |
|||
outType = null; |
|||
} |
|||
else if (taskParameter.GetTypeInfo().IsGenericType |
|||
&& taskParameter.GetGenericTypeDefinition() == s_parameterTaskType) |
|||
{ |
|||
valid = true; |
|||
outType = taskParameter.GetGenericArguments()[0]; |
|||
if (outType.IsGenericParameter) |
|||
{ |
|||
outType = s_objectType; |
|||
isGenericOut = true; |
|||
} |
|||
InspectParameterType(outType, member.ReturnParameter, out outSignature, out outArguments); |
|||
} |
|||
} |
|||
if (!valid) |
|||
{ |
|||
throw new ArgumentException($"DBus Method {memberName} does not return 'Task'/'Task<>'"); |
|||
} |
|||
|
|||
IList<ArgumentDescription> inArguments = null; |
|||
Signature? inSignature = null; |
|||
var parameters = member.GetParameters(); |
|||
if (parameters.Length > 0 && parameters[parameters.Length - 1].ParameterType == s_cancellationTokenType) |
|||
{ |
|||
throw new NotSupportedException($"DBus Method {memberName} does not support cancellation. See https://github.com/tmds/Tmds.DBus/issues/15."); |
|||
} |
|||
|
|||
for (int i = 0; i < parameters.Length; i++) |
|||
{ |
|||
var param = parameters[i]; |
|||
var parameterType = param.ParameterType; |
|||
var paramSignature = Signature.GetSig(parameterType, isCompileTimeType: true); |
|||
if (inSignature == null) |
|||
{ |
|||
inSignature = paramSignature; |
|||
} |
|||
else |
|||
{ |
|||
inSignature = Signature.Concat(inSignature.Value, paramSignature); |
|||
} |
|||
inArguments = inArguments ?? new List<ArgumentDescription>(); |
|||
var argumentAttribute = param.GetCustomAttribute<ArgumentAttribute>(false); |
|||
var argName = argumentAttribute != null ? argumentAttribute.Name : param.Name; |
|||
inArguments.Add(new ArgumentDescription(argName, paramSignature, parameterType)); |
|||
} |
|||
|
|||
var methodDescription = new MethodDescription(member, name, inArguments, inSignature, outType, isGenericOut, outSignature, outArguments); |
|||
if (member.Name == interfaceAttribute.GetPropertyMethod) |
|||
{ |
|||
if (propertyGetMethod != null) |
|||
{ |
|||
throw new ArgumentException($"Multiple property Get methods are declared: {memberName}, {propertyGetMethod.MethodInfo.ToString()}"); |
|||
} |
|||
propertyGetMethod = methodDescription; |
|||
if ((propertyGetMethod.InSignature != Signature.StringSig) || |
|||
(propertyGetMethod.OutSignature != Signature.VariantSig)) |
|||
{ |
|||
throw new ArgumentException($"Property Get method {memberName} must accept a 'string' parameter and return 'Task<object>'"); |
|||
} |
|||
} |
|||
else if (member.Name == interfaceAttribute.GetAllPropertiesMethod) |
|||
{ |
|||
if (propertyGetAllMethod != null) |
|||
{ |
|||
throw new ArgumentException($"Multiple property GetAll are declared: {memberName}, {propertyGetAllMethod.MethodInfo.ToString()}"); |
|||
} |
|||
propertyGetAllMethod = methodDescription; |
|||
if ((propertyGetAllMethod.InArguments.Count != 0) || |
|||
(propertyGetAllMethod.OutSignature != s_getAllOutSignature)) |
|||
{ |
|||
throw new ArgumentException($"Property GetAll method {memberName} must accept no parameters and return 'Task<IDictionary<string, object>>'"); |
|||
} |
|||
if (propertyType == null) |
|||
{ |
|||
if (ArgTypeInspector.InspectEnumerableType(methodDescription.OutType, out elementType, isCompileTimeType: true) == ArgTypeInspector.EnumerableType.AttributeDictionary) |
|||
{ |
|||
propertyType = methodDescription.OutType; |
|||
} |
|||
} |
|||
} |
|||
else if (member.Name == interfaceAttribute.SetPropertyMethod) |
|||
{ |
|||
if (propertySetMethod != null) |
|||
{ |
|||
throw new ArgumentException($"Multiple property Set are declared: {memberName}, {propertySetMethod.MethodInfo.ToString()}"); |
|||
} |
|||
propertySetMethod = methodDescription; |
|||
if ((propertySetMethod.InArguments?.Count != 2 || propertySetMethod.InArguments[0].Type != s_stringType || propertySetMethod.InArguments[1].Type != s_objectType) || |
|||
(propertySetMethod.OutArguments.Count != 0)) |
|||
{ |
|||
throw new ArgumentException($"Property Set method {memberName} must accept a 'string' and 'object' parameter and return 'Task'"); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
methods = methods ?? new List<MethodDescription>(); |
|||
methods.Add(methodDescription); |
|||
} |
|||
} |
|||
} |
|||
if (propertyType != null) |
|||
{ |
|||
var fields = propertyType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); |
|||
foreach(var field in fields) |
|||
{ |
|||
string propertyName; |
|||
Type fieldType; |
|||
PropertyTypeInspector.InspectField(field, out propertyName, out fieldType); |
|||
var propertySignature = Signature.GetSig(fieldType, isCompileTimeType: true); |
|||
var propertyAccess = field.GetCustomAttribute<PropertyAttribute>()?.Access ?? PropertyAccess.ReadWrite; |
|||
var description = new PropertyDescription(propertyName, propertySignature, propertyAccess); |
|||
properties = properties ?? new List<PropertyDescription>(); |
|||
properties.Add(description); |
|||
} |
|||
} |
|||
interfaces.Add(new InterfaceDescription(type, interfaceAttribute.Name, methods, signals, properties, |
|||
propertyGetMethod, propertyGetAllMethod, propertySetMethod, propertiesChangedSignal)); |
|||
} |
|||
|
|||
private static void InspectParameterType(Type parameterType, ParameterInfo parameter, out Signature? signature, out IList<ArgumentDescription> arguments) |
|||
{ |
|||
var argumentAttribute = parameter.GetCustomAttribute<ArgumentAttribute>(false); |
|||
bool isValueTuple; |
|||
arguments = new List<ArgumentDescription>(); |
|||
if (argumentAttribute != null) |
|||
{ |
|||
signature = Signature.GetSig(parameterType, isCompileTimeType: true); |
|||
arguments.Add(new ArgumentDescription(argumentAttribute.Name, signature.Value, parameterType)); |
|||
} |
|||
else if (IsStructType(parameterType, out isValueTuple)) |
|||
{ |
|||
signature = null; |
|||
var fields = ArgTypeInspector.GetStructFields(parameterType, isValueTuple); |
|||
IList<string> tupleElementNames = null; |
|||
if (isValueTuple) |
|||
{ |
|||
var tupleElementNamesAttribute = parameter.GetCustomAttribute<TupleElementNamesAttribute>(false); |
|||
if (tupleElementNamesAttribute != null) |
|||
{ |
|||
tupleElementNames = tupleElementNamesAttribute.TransformNames; |
|||
} |
|||
} |
|||
int nameIdx = 0; |
|||
for (int i = 0; i < fields.Length;) |
|||
{ |
|||
var field = fields[i]; |
|||
var fieldType = field.FieldType; |
|||
if (i == 7 && isValueTuple) |
|||
{ |
|||
fields = ArgTypeInspector.GetStructFields(fieldType, isValueTuple); |
|||
i = 0; |
|||
} |
|||
else |
|||
{ |
|||
var argumentSignature = Signature.GetSig(fieldType, isCompileTimeType: true); |
|||
var name = tupleElementNames != null && tupleElementNames.Count > nameIdx ? tupleElementNames[nameIdx] : field.Name; |
|||
arguments.Add(new ArgumentDescription(name, argumentSignature, fieldType)); |
|||
if (signature == null) |
|||
{ |
|||
signature = argumentSignature; |
|||
} |
|||
else |
|||
{ |
|||
signature = Signature.Concat(signature.Value, argumentSignature); |
|||
} |
|||
i++; |
|||
nameIdx++; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
signature = Signature.GetSig(parameterType, isCompileTimeType: true); |
|||
arguments.Add(new ArgumentDescription("value", signature.Value, parameterType)); |
|||
} |
|||
} |
|||
|
|||
private static bool IsStructType(Type type, out bool isValueTuple) |
|||
{ |
|||
isValueTuple = false; |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
return false; |
|||
} |
|||
if (s_mappedTypes.Contains(type)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType: true)) |
|||
{ |
|||
return false; |
|||
} |
|||
Type elementType; |
|||
if (ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType: true) |
|||
!= ArgTypeInspector.EnumerableType.NotEnumerable) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return ArgTypeInspector.IsStructType(type, out isValueTuple); |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
static class TypeExtensions |
|||
{ |
|||
public static ConstructorInfo GetConstructor(this Type type, BindingFlags bindingFlags, Type[] types) |
|||
{ |
|||
var constructors = type.GetConstructors(bindingFlags); |
|||
return (from constructor in constructors |
|||
let parameters = constructor.GetParameters() |
|||
let parameterTypes = parameters.Select(p => p.ParameterType).ToArray() |
|||
where types.SequenceEqual(parameterTypes) |
|||
select constructor).Single(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,157 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Reflection; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.CodeGen |
|||
{ |
|||
internal delegate void WriteMethodDelegate<in T>(MessageWriter writer, T value); |
|||
|
|||
internal class WriteMethodFactory |
|||
{ |
|||
public static WriteMethodDelegate<T> CreateWriteMethodDelegate<T>() |
|||
{ |
|||
var type = typeof(T); |
|||
var writeMethod = CreateWriteMethodForType(type, true); |
|||
return (WriteMethodDelegate<T>)writeMethod.CreateDelegate(typeof(WriteMethodDelegate<T>)); |
|||
} |
|||
|
|||
public static MethodInfo CreateWriteMethodForType(Type type, bool isCompileTimeType) // void Write(MessageWriter, T)
|
|||
{ |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
type = Enum.GetUnderlyingType(type); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
return s_messageWriterWriteBoolean; |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return s_messageWriterWriteByte; |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return s_messageWriterWriteDouble; |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return s_messageWriterWriteInt16; |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return s_messageWriterWriteInt32; |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return s_messageWriterWriteInt64; |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
return s_messageWriterWriteObjectPath; |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
return s_messageWriterWriteSignature; |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
return s_messageWriterWriteString; |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return s_messageWriterWriteSingle; |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return s_messageWriterWriteUInt16; |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return s_messageWriterWriteUInt32; |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return s_messageWriterWriteUInt64; |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
return s_messageWriterWriteVariant; |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
return s_messageWriterWriteBusObject; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType)) |
|||
{ |
|||
return s_messageWriterWriteBusObject; |
|||
} |
|||
|
|||
Type elementType; |
|||
var enumerableType = ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType); |
|||
if (enumerableType != ArgTypeInspector.EnumerableType.NotEnumerable) |
|||
{ |
|||
if ((enumerableType == ArgTypeInspector.EnumerableType.EnumerableKeyValuePair) || |
|||
(enumerableType == ArgTypeInspector.EnumerableType.GenericDictionary)) |
|||
{ |
|||
return s_messageWriterWriteDict.MakeGenericMethod(elementType.GenericTypeArguments); |
|||
} |
|||
else if (enumerableType == ArgTypeInspector.EnumerableType.AttributeDictionary) |
|||
{ |
|||
return s_messageWriterWriteDictionaryObject.MakeGenericMethod(type); |
|||
} |
|||
else // Enumerable
|
|||
{ |
|||
return s_messageWriterWriteArray.MakeGenericMethod(new[] { elementType }); |
|||
} |
|||
} |
|||
|
|||
bool isValueTuple; |
|||
if (ArgTypeInspector.IsStructType(type, out isValueTuple)) |
|||
{ |
|||
if (isValueTuple) |
|||
{ |
|||
return s_messageWriterWriteValueTupleStruct.MakeGenericMethod(type); |
|||
} |
|||
else |
|||
{ |
|||
return s_messageWriterWriteStruct.MakeGenericMethod(type); |
|||
} |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsSafeHandleType(type)) |
|||
{ |
|||
return s_messageWriterWriteSafeHandle; |
|||
} |
|||
|
|||
throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'"); |
|||
} |
|||
|
|||
private static readonly MethodInfo s_messageWriterWriteBoolean = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteBoolean)); |
|||
private static readonly MethodInfo s_messageWriterWriteByte = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteByte)); |
|||
private static readonly MethodInfo s_messageWriterWriteDouble = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteDouble)); |
|||
private static readonly MethodInfo s_messageWriterWriteInt16 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteInt16)); |
|||
private static readonly MethodInfo s_messageWriterWriteInt32 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteInt32)); |
|||
private static readonly MethodInfo s_messageWriterWriteInt64 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteInt64)); |
|||
private static readonly MethodInfo s_messageWriterWriteObjectPath = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteObjectPath)); |
|||
private static readonly MethodInfo s_messageWriterWriteSignature = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteSignature)); |
|||
private static readonly MethodInfo s_messageWriterWriteSafeHandle = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteSafeHandle)); |
|||
private static readonly MethodInfo s_messageWriterWriteSingle = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteSingle)); |
|||
private static readonly MethodInfo s_messageWriterWriteString = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteString)); |
|||
private static readonly MethodInfo s_messageWriterWriteUInt16 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteUInt16)); |
|||
private static readonly MethodInfo s_messageWriterWriteUInt32 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteUInt32)); |
|||
private static readonly MethodInfo s_messageWriterWriteUInt64 = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteUInt64)); |
|||
private static readonly MethodInfo s_messageWriterWriteVariant = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteVariant)); |
|||
private static readonly MethodInfo s_messageWriterWriteBusObject = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteBusObject)); |
|||
private static readonly MethodInfo s_messageWriterWriteArray = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteArray)); |
|||
private static readonly MethodInfo s_messageWriterWriteDict = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteFromDict)); |
|||
private static readonly MethodInfo s_messageWriterWriteDictionaryObject = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteDictionaryObject)); |
|||
private static readonly MethodInfo s_messageWriterWriteStruct = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteStructure)); |
|||
private static readonly MethodInfo s_messageWriterWriteValueTupleStruct = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteValueTupleStructure)); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Exception thrown when the D-Bus connection cannot be succesfully established.
|
|||
/// </summary>
|
|||
public class ConnectException : Exception |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an instance of the ConnectException with the specified message.
|
|||
/// </summary>
|
|||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
|||
public ConnectException(string message) : base(message) |
|||
{ } |
|||
|
|||
/// <summary>
|
|||
/// Creates an instance of the ConnectException with the specified message and innerException.
|
|||
/// </summary>
|
|||
/// <param name="message">The error message that explains the reason for the exception..</param>
|
|||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
|
|||
public ConnectException(string message, Exception innerException) : base(message, innerException) |
|||
{ } |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,31 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Information about established Connection.
|
|||
/// </summary>
|
|||
public class ConnectionInfo |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an instance of ConnectionInfo.
|
|||
/// </summary>
|
|||
/// <param name="localName">Name assigned by the bus to the connection.</param>
|
|||
public ConnectionInfo(string localName) |
|||
{ |
|||
LocalName = localName; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Local name assigned by the bus to the connection.
|
|||
/// </summary>
|
|||
public string LocalName { get; } |
|||
|
|||
/// <summary>
|
|||
/// Returns whether the remote peer is a bus.
|
|||
/// </summary>
|
|||
public bool RemoteIsBus => !string.IsNullOrEmpty(LocalName); |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Options that configure the behavior of a Connection.
|
|||
/// </summary>
|
|||
public abstract class ConnectionOptions |
|||
{ |
|||
internal ConnectionOptions() |
|||
{} |
|||
|
|||
/// <summary>
|
|||
/// SynchronizationContext used for event handlers and callbacks.
|
|||
/// </summary>
|
|||
public SynchronizationContext SynchronizationContext { get; set; } |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// State of the Connection.
|
|||
/// </summary>
|
|||
public enum ConnectionState |
|||
{ |
|||
/// <summary>No connection attempt has been made.</summary>
|
|||
Created, |
|||
/// <summary>Connecting to remote peer.</summary>
|
|||
Connecting, |
|||
/// <summary>Connection established.</summary>
|
|||
Connected, |
|||
/// <summary>Connection is closing.</summary>
|
|||
Disconnecting, |
|||
/// <summary>Connection is closed.</summary>
|
|||
Disconnected |
|||
} |
|||
} |
|||
@ -1,48 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Event data for the Connection StateChanged event.
|
|||
/// </summary>
|
|||
public struct ConnectionStateChangedEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an instance of ConnectionStateChangedEventArgs.
|
|||
/// </summary>
|
|||
/// <param name="state">State of the connection.</param>
|
|||
/// <param name="disconnectReason">Reason the connection closed.</param>
|
|||
/// <param name="connectionInfo">Information about established connection.</param>
|
|||
public ConnectionStateChangedEventArgs(ConnectionState state, Exception disconnectReason, ConnectionInfo connectionInfo) |
|||
{ |
|||
State = state; |
|||
DisconnectReason = disconnectReason; |
|||
ConnectionInfo = connectionInfo; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ConnectionInfo for established connection.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This property is set for the Connected event.
|
|||
/// </remarks>
|
|||
public ConnectionInfo ConnectionInfo { get; } |
|||
|
|||
/// <summary>
|
|||
/// New connection state.
|
|||
/// </summary>
|
|||
public ConnectionState State { get; } |
|||
|
|||
/// <summary>
|
|||
/// Reason the connection closed.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This property is set for the Disconnecting, Disconnected and following Connecting event.
|
|||
/// </remarks>
|
|||
public Exception DisconnectReason { get; } |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,38 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the D-Bus error message which is used to signal the unsuccesfull invocation of a method.
|
|||
/// </summary>
|
|||
public class DBusException : Exception |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new DBusException with the given name and message.
|
|||
/// </summary>
|
|||
/// <param name="errorName">Name of the error</param>
|
|||
/// <param name="errorMessage">Message of the error</param>
|
|||
public DBusException(string errorName, string errorMessage) : |
|||
base($"{errorName}: {errorMessage}") |
|||
{ |
|||
ErrorName = errorName; |
|||
ErrorMessage = errorMessage; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Name of the error.
|
|||
/// </summary>
|
|||
public string ErrorName { get; } |
|||
|
|||
/// <summary>
|
|||
/// Message of the error.
|
|||
/// </summary>
|
|||
public string ErrorMessage { get; } |
|||
} |
|||
} |
|||
@ -1,59 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Provides information for mapping the C# interface to a D-Bus interface.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] |
|||
public sealed class DBusInterfaceAttribute : Attribute |
|||
{ |
|||
/// <summary>
|
|||
/// Name of the D-Bus interface.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Method name of the property get method. Defaults to <c>GetAsync</c>.
|
|||
/// </summary>
|
|||
public string GetPropertyMethod { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Method name of the property get method. Defaults to <c>SetAsync</c>.
|
|||
/// </summary>
|
|||
public string SetPropertyMethod { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Method name of the property get all method. Defaults to <c>GetAllAsync</c>.
|
|||
/// </summary>
|
|||
public string GetAllPropertiesMethod { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Method name of the property get all method. Defaults to <c>WatchPropertiesAsync</c>.
|
|||
/// </summary>
|
|||
public string WatchPropertiesMethod { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Set to a type decorated with the Dictionary attribute used to provide property introspection information. When unset the type returned by the <c>GetAllPropertiesMethod</c> is used.
|
|||
/// </summary>
|
|||
public Type PropertyType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Creates a DBusInterfaceAttribute with the specified D-Bus interface name.
|
|||
/// </summary>
|
|||
/// <param name="name">D-Bus interface name</param>
|
|||
public DBusInterfaceAttribute(string name) |
|||
{ |
|||
Name = name; |
|||
GetAllPropertiesMethod = "GetAllAsync"; |
|||
SetPropertyMethod = "SetAsync"; |
|||
GetPropertyMethod = "GetAsync"; |
|||
WatchPropertiesMethod = "WatchPropertiesAsync"; |
|||
PropertyType = null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Indicates the type must be marshalled as a D-Bus dictionary of <c>a{sv}</c>.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] |
|||
public sealed class DictionaryAttribute : Attribute |
|||
{} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Exception thrown when the D-Bus connection was closed after being succesfully established. When the connection is
|
|||
/// closed during the connect operation, ConnectException is thrown instead.
|
|||
/// </summary>
|
|||
public class DisconnectedException : Exception |
|||
{ |
|||
internal DisconnectedException(Exception innerException) : base(innerException.Message, innerException) |
|||
{ } |
|||
} |
|||
} |
|||
@ -1,77 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Runtime.InteropServices; |
|||
#if NET6_0_OR_GREATER
|
|||
using System.Runtime.Versioning; |
|||
#endif
|
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal static class Environment |
|||
{ |
|||
private const string MachineUuidPath = @"/var/lib/dbus/machine-id"; |
|||
|
|||
public static readonly EndianFlag NativeEndianness; |
|||
|
|||
#if NET6_0_OR_GREATER
|
|||
[SupportedOSPlatformGuard("windows")] |
|||
#endif
|
|||
public static readonly bool IsWindows; |
|||
|
|||
static Environment() |
|||
{ |
|||
NativeEndianness = BitConverter.IsLittleEndian ? EndianFlag.Little : EndianFlag.Big; |
|||
IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); |
|||
} |
|||
|
|||
private static string _machineId; |
|||
public static string MachineId |
|||
{ |
|||
get |
|||
{ |
|||
if (_machineId != null) |
|||
{ |
|||
return _machineId; |
|||
} |
|||
if (File.Exists(MachineUuidPath)) |
|||
{ |
|||
_machineId = Guid.Parse(File.ReadAllText(MachineUuidPath).Substring(0, 32)).ToString("N"); |
|||
} |
|||
else |
|||
{ |
|||
_machineId = Guid.Empty.ToString("N"); |
|||
} |
|||
return _machineId; |
|||
} |
|||
} |
|||
|
|||
private static string _uid; |
|||
public static string UserId |
|||
{ |
|||
get |
|||
{ |
|||
if (_uid != null) |
|||
{ |
|||
return _uid; |
|||
} |
|||
|
|||
if (Environment.IsWindows) |
|||
{ |
|||
_uid = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; |
|||
} |
|||
else |
|||
{ |
|||
_uid = Interop.geteuid().ToString(); |
|||
} |
|||
|
|||
return _uid; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,76 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Interface of the Connection class.
|
|||
/// </summary>
|
|||
public interface IConnection : IDisposable |
|||
{ |
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<ConnectionInfo> ConnectAsync(); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
T CreateProxy<T>(string serviceName, ObjectPath2 path2); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
event EventHandler<ConnectionStateChangedEventArgs> StateChanged; |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<string[]> ListServicesAsync(); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<string[]> ListActivatableServicesAsync(); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<string> ResolveServiceOwnerAsync(string serviceName); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<IDisposable> ResolveServiceOwnerAsync(string serviceName, Action<ServiceOwnerChangedEventArgs> handler, Action<Exception> onError = null); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<ServiceStartResult> ActivateServiceAsync(string serviceName); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<bool> IsServiceActiveAsync(string serviceName); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task QueueServiceRegistrationAsync(string serviceName, Action onAquired = null, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task QueueServiceRegistrationAsync(string serviceName, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task RegisterServiceAsync(string serviceName, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task RegisterServiceAsync(string serviceName, ServiceRegistrationOptions options); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task<bool> UnregisterServiceAsync(string serviceName); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task RegisterObjectAsync(IDBusObject o); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
Task RegisterObjectsAsync(IEnumerable<IDBusObject> objects); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
void UnregisterObject(ObjectPath2 path2); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
void UnregisterObject(IDBusObject dbusObject); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
void UnregisterObjects(IEnumerable<ObjectPath2> paths); |
|||
|
|||
/// <summary><see cref="Connection2"/></summary>
|
|||
void UnregisterObjects(IEnumerable<IDBusObject> objects); |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
[DBusInterface(DBusConnection.DBusInterface)] |
|||
internal interface IDBus : IDBusObject |
|||
{ |
|||
Task<string[]> ListActivatableNamesAsync(); |
|||
Task<bool> NameHasOwnerAsync(string name); |
|||
Task<ServiceStartResult> StartServiceByNameAsync(string name, uint flags); |
|||
Task<string> GetNameOwnerAsync(string name); |
|||
Task<string[]> ListNamesAsync(); |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Base interface for D-Bus objects.
|
|||
/// </summary>
|
|||
public interface IDBusObject |
|||
{ |
|||
/// <summary>
|
|||
/// Path of the D-Bus object.
|
|||
/// </summary>
|
|||
ObjectPath2 ObjectPath2 { get; } |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
using SizeT = System.UIntPtr; |
|||
using SSizeT = System.IntPtr; |
|||
internal static class Interop |
|||
{ |
|||
public struct Passwd |
|||
{ |
|||
public IntPtr Name; |
|||
public IntPtr Password; |
|||
public uint UserID; |
|||
public uint GroupID; |
|||
public IntPtr UserInfo; |
|||
public IntPtr HomeDir; |
|||
public IntPtr Shell; |
|||
} |
|||
|
|||
[DllImport("libc")] |
|||
internal static extern uint geteuid(); |
|||
[DllImport ("libc")] |
|||
internal static extern uint getuid(); |
|||
[DllImport("libc")] |
|||
internal static extern unsafe int getpwuid_r(uint uid, out Passwd pwd, byte* buf, int bufLen, out IntPtr result); |
|||
[DllImport ("libc", SetLastError=true)] |
|||
public static extern SSizeT sendmsg(int sockfd, IntPtr msg, int flags); |
|||
[DllImport ("libc", SetLastError=true)] |
|||
public static extern SSizeT recvmsg(int sockfd, IntPtr msg, int flags); |
|||
|
|||
[DllImport("libX11")] |
|||
internal static extern IntPtr XOpenDisplay (string name); |
|||
[DllImport("libX11")] |
|||
internal static extern int XCloseDisplay (IntPtr display); |
|||
[DllImport("libX11")] |
|||
internal static extern IntPtr XInternAtom (IntPtr display, string atom_name, bool only_if_exists); |
|||
[DllImport("libX11")] |
|||
internal static extern int XGetWindowProperty(IntPtr display, IntPtr w, IntPtr property, |
|||
int long_offset, int long_length, bool delete, IntPtr req_type, |
|||
out IntPtr actual_type_return, out IntPtr actual_format_return, |
|||
out IntPtr nitems_return, out IntPtr bytes_after_return, out IntPtr prop_return); |
|||
[DllImport("libX11")] |
|||
internal static extern int XFree(IntPtr data); |
|||
[DllImport("libX11")] |
|||
internal static extern IntPtr XGetSelectionOwner(IntPtr display, IntPtr Atom); |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal delegate void MessageHandler(Message message); |
|||
} |
|||
@ -1,62 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal static class MessageHelper |
|||
{ |
|||
public static Message ConstructErrorReply(Message incoming, string errorName, string errorMessage) |
|||
{ |
|||
MessageWriter writer = new MessageWriter(incoming.Header.Endianness); |
|||
writer.WriteString(errorMessage); |
|||
|
|||
Message replyMessage = new Message( |
|||
new Header(MessageType.Error) |
|||
{ |
|||
ErrorName = errorName, |
|||
ReplySerial = incoming.Header.Serial, |
|||
Signature = Signature.StringSig, |
|||
Destination = incoming.Header.Sender |
|||
}, |
|||
writer.ToArray(), |
|||
writer.UnixFds |
|||
); |
|||
|
|||
return replyMessage; |
|||
} |
|||
|
|||
public static Message ConstructReply(Message msg, params object[] vals) |
|||
{ |
|||
Signature inSig = Signature.GetSig(vals); |
|||
|
|||
MessageWriter writer = null; |
|||
if (vals != null && vals.Length != 0) |
|||
{ |
|||
writer = new MessageWriter(Environment.NativeEndianness); |
|||
|
|||
foreach (object arg in vals) |
|||
writer.Write(arg.GetType(), arg, isCompileTimeType: false); |
|||
} |
|||
|
|||
Message replyMsg = new Message( |
|||
new Header(MessageType.MethodReturn) |
|||
{ |
|||
ReplySerial = msg.Header.Serial, |
|||
Signature = inSig |
|||
}, |
|||
writer?.ToArray(), |
|||
writer?.UnixFds |
|||
); |
|||
|
|||
if (msg.Header.Sender != null) |
|||
replyMsg.Header.Destination = msg.Header.Sender; |
|||
|
|||
return replyMsg; |
|||
} |
|||
} |
|||
} |
|||
@ -1,179 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Path to D-Bus object.
|
|||
/// </summary>
|
|||
public struct ObjectPath2 : IComparable, IComparable<ObjectPath2>, IEquatable<ObjectPath2> |
|||
{ |
|||
/// <summary>
|
|||
/// Root path (<c>"/"</c>).
|
|||
/// </summary>
|
|||
public static readonly ObjectPath2 Root = new ObjectPath2("/"); |
|||
|
|||
internal readonly string Value; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new ObjectPath.
|
|||
/// </summary>
|
|||
/// <param name="value">path.</param>
|
|||
public ObjectPath2(string value) |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException("value"); |
|||
|
|||
Validate(value); |
|||
|
|||
this.Value = value; |
|||
} |
|||
|
|||
static void Validate(string value) |
|||
{ |
|||
if (!value.StartsWith("/", StringComparison.Ordinal)) |
|||
throw new ArgumentException("value"); |
|||
if (value.EndsWith("/", StringComparison.Ordinal) && value.Length > 1) |
|||
throw new ArgumentException("ObjectPath cannot end in '/'"); |
|||
|
|||
bool multipleSlash = false; |
|||
|
|||
foreach (char c in value) |
|||
{ |
|||
bool valid = (c >= 'a' && c <= 'z') |
|||
|| (c >= 'A' && c <= 'Z') |
|||
|| (c >= '0' && c <= '9') |
|||
|| c == '_' |
|||
|| (!multipleSlash && c == '/'); |
|||
|
|||
if (!valid) |
|||
{ |
|||
var message = string.Format("'{0}' is not a valid character in an ObjectPath", c); |
|||
throw new ArgumentException(message, "value"); |
|||
} |
|||
|
|||
multipleSlash = c == '/'; |
|||
} |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares the current instance with another object of the same type and returns an integer that
|
|||
/// indicates whether the current instance precedes, follows, or occurs in the same position in
|
|||
/// the sort order as the other object.
|
|||
/// </summary>
|
|||
public int CompareTo(ObjectPath2 other) |
|||
{ |
|||
return Value.CompareTo(other.Value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares the current instance with another object of the same type and returns an integer that
|
|||
/// indicates whether the current instance precedes, follows, or occurs in the same position in
|
|||
/// the sort order as the other object.
|
|||
/// </summary>
|
|||
public int CompareTo(object otherObject) |
|||
{ |
|||
var other = otherObject as ObjectPath2?; |
|||
|
|||
if (other == null) |
|||
return 1; |
|||
|
|||
return Value.CompareTo(other.Value.Value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified object is equal to the current object.
|
|||
/// </summary>
|
|||
public bool Equals(ObjectPath2 other) |
|||
{ |
|||
return Value == other.Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified object is equal to the current object.
|
|||
/// </summary>
|
|||
public override bool Equals(object o) |
|||
{ |
|||
var b = o as ObjectPath2?; |
|||
|
|||
if (b == null) |
|||
return false; |
|||
|
|||
return Value.Equals(b.Value.Value, StringComparison.Ordinal); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether two specified ObjectPaths have the same value.
|
|||
/// </summary>
|
|||
public static bool operator==(ObjectPath2 a, ObjectPath2 b) |
|||
{ |
|||
return a.Value == b.Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether two specified ObjectPaths have different values.
|
|||
/// </summary>
|
|||
public static bool operator!=(ObjectPath2 a, ObjectPath2 b) |
|||
{ |
|||
return !(a == b); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this ObjectPath.
|
|||
/// </summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
if (Value == null) |
|||
{ |
|||
return 0; |
|||
} |
|||
return Value.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a string that represents the current object.
|
|||
/// </summary>
|
|||
public override string ToString() |
|||
{ |
|||
return Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the ObjectPath that is represented by the string value.
|
|||
/// </summary>
|
|||
/// <param name="value">path.</param>
|
|||
public static implicit operator ObjectPath2(string value) |
|||
{ |
|||
return new ObjectPath2(value); |
|||
} |
|||
|
|||
//this may or may not prove useful
|
|||
internal string[] Decomposed |
|||
{ |
|||
get |
|||
{ |
|||
return Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); |
|||
} |
|||
} |
|||
|
|||
internal ObjectPath2 Parent |
|||
{ |
|||
get |
|||
{ |
|||
if (Value == Root.Value) |
|||
return null; |
|||
|
|||
string par = Value.Substring(0, Value.LastIndexOf('/')); |
|||
if (par == String.Empty) |
|||
par = "/"; |
|||
|
|||
return new ObjectPath2(par); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,76 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal class OwnerChangedMatchRule : SignalMatchRule |
|||
{ |
|||
public OwnerChangedMatchRule(string serviceName) |
|||
{ |
|||
ServiceName = serviceName; |
|||
Interface = DBusConnection.DBusInterface; |
|||
Member = "NameOwnerChanged"; |
|||
Path = DBusConnection.DBusObjectPath2; |
|||
} |
|||
|
|||
public string ServiceName { get; set; } |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
int hash = base.GetHashCode(); |
|||
hash = hash * 23 + (ServiceName == null ? 0 : ServiceName.GetHashCode()); |
|||
return hash; |
|||
} |
|||
|
|||
public override bool Equals(object o) |
|||
{ |
|||
OwnerChangedMatchRule r = o as OwnerChangedMatchRule; |
|||
if (o == null) |
|||
return false; |
|||
|
|||
return Interface == r.Interface && |
|||
Member == r.Member && |
|||
Path == r.Path && |
|||
ServiceName == r.ServiceName; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
StringBuilder sb = new StringBuilder(); |
|||
|
|||
Append(sb, "type", "signal"); |
|||
|
|||
if (Interface != null) |
|||
{ |
|||
Append(sb, "interface", Interface); |
|||
} |
|||
if (Member != null) |
|||
{ |
|||
Append(sb, "member", Member); |
|||
} |
|||
if (Path != null) |
|||
{ |
|||
Append(sb, "path", Path.Value); |
|||
} |
|||
if (ServiceName != null) |
|||
{ |
|||
if (ServiceName == ".*") |
|||
{} |
|||
else if (ServiceName.EndsWith(".*", StringComparison.Ordinal)) |
|||
{ |
|||
Append(sb, "arg0namespace", ServiceName.Substring(0, ServiceName.Length - 2)); |
|||
} |
|||
else |
|||
{ |
|||
Append(sb, "arg0", ServiceName); |
|||
} |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// The mutability of a property
|
|||
/// </summary>
|
|||
public enum PropertyAccess |
|||
{ |
|||
/// <summary>
|
|||
/// Allows the property to be read and written
|
|||
/// </summary>
|
|||
ReadWrite, |
|||
/// <summary>
|
|||
/// Allows the property to only be read
|
|||
/// </summary>
|
|||
Read, |
|||
/// <summary>
|
|||
/// Allows the property to only be written to
|
|||
/// </summary>
|
|||
Write |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
using System; |
|||
using Tmds.DBus.CodeGen; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Overrides how the property is handled.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Field)] |
|||
public sealed class PropertyAttribute : Attribute |
|||
{ |
|||
/// <summary>
|
|||
/// If not null, used to override the autogenerated name.
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Specifies the mutability of the property.
|
|||
/// </summary>
|
|||
public PropertyAccess Access { get; set; } = PropertyAccess.ReadWrite; |
|||
} |
|||
} |
|||
@ -1,85 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Event data for the properties changed event.
|
|||
/// </summary>
|
|||
public struct PropertyChanges |
|||
{ |
|||
private KeyValuePair<string, object>[] _changed; |
|||
private string[] _invalidated; |
|||
|
|||
/// <summary>
|
|||
/// Properties that have changed with their new value.
|
|||
/// </summary>
|
|||
public KeyValuePair<string, object>[] Changed => _changed; |
|||
|
|||
/// <summary>
|
|||
/// Properties that have changed.
|
|||
/// </summary>
|
|||
public string[] Invalidated => _invalidated; |
|||
|
|||
/// <summary>
|
|||
/// Creates a PropertyChanges event.
|
|||
/// </summary>
|
|||
/// <param name="changed">Properties that changed with their new value.</param>
|
|||
/// <param name="invalidated">Properties that changed without providing new value.</param>
|
|||
public PropertyChanges(KeyValuePair<string, object>[] changed,string[] invalidated = null) |
|||
{ |
|||
_changed = changed ?? Array.Empty<KeyValuePair<string, object>>(); |
|||
_invalidated = invalidated ?? Array.Empty<string>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a PropertyChanges event for a single value change.
|
|||
/// </summary>
|
|||
public static PropertyChanges ForProperty(string prop, object val) |
|||
=> new PropertyChanges(new [] { new KeyValuePair<string, object>(prop, val) }); |
|||
|
|||
/// <summary>
|
|||
/// Retrieves value for a specific property. <c>default(T)</c> is returned when the property is not present.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the property.</typeparam>
|
|||
/// <param name="name">Property name.</param>
|
|||
/// <returns>
|
|||
/// Value of the property. <c>default(T)</c> when property not present.
|
|||
/// </returns>
|
|||
public T Get<T>(string name) where T : class |
|||
{ |
|||
for (int i = 0; i < Changed.Length; i++) |
|||
{ |
|||
if (Changed[i].Key == name) |
|||
{ |
|||
return (T)Changed[i].Value; |
|||
} |
|||
} |
|||
return default(T); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves value for a specific property. <c>default(T?)</c> is returned when the property is not present.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the property.</typeparam>
|
|||
/// <param name="name">Property name.</param>
|
|||
/// <returns>
|
|||
/// Value of the property. <c>default(T?)</c> when property not present.
|
|||
/// </returns>
|
|||
public T? GetStruct<T>(string name) where T : struct |
|||
{ |
|||
for (int i = 0; i < Changed.Length; i++) |
|||
{ |
|||
if (Changed[i].Key == name) |
|||
{ |
|||
return (T)Changed[i].Value; |
|||
} |
|||
} |
|||
return default(T?); |
|||
} |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal enum DType : byte |
|||
{ |
|||
Invalid = (byte)'\0', |
|||
|
|||
Byte = (byte)'y', |
|||
Boolean = (byte)'b', |
|||
Int16 = (byte)'n', |
|||
UInt16 = (byte)'q', |
|||
Int32 = (byte)'i', |
|||
UInt32 = (byte)'u', |
|||
Int64 = (byte)'x', |
|||
UInt64 = (byte)'t', |
|||
Single = (byte)'f', //This is not yet supported!
|
|||
Double = (byte)'d', |
|||
String = (byte)'s', |
|||
ObjectPath = (byte)'o', |
|||
Signature = (byte)'g', |
|||
UnixFd = (byte)'h', |
|||
Array = (byte)'a', |
|||
Variant = (byte)'v', |
|||
|
|||
StructBegin = (byte)'(', |
|||
StructEnd = (byte)')', |
|||
DictEntryBegin = (byte)'{', |
|||
DictEntryEnd = (byte)'}', |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal enum EndianFlag : byte |
|||
{ |
|||
Little = (byte)'l', |
|||
Big = (byte)'B', |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal enum FieldCode : byte |
|||
{ |
|||
Invalid = 0, |
|||
Path = 1, |
|||
Interface = 2, |
|||
Member = 3, |
|||
ErrorName = 4, |
|||
ReplySerial = 5, |
|||
Destination = 6, |
|||
Sender = 7, |
|||
Signature = 8, |
|||
UnixFds = 9 |
|||
} |
|||
} |
|||
@ -1,180 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal class Header |
|||
{ |
|||
public Header(MessageType type, EndianFlag endianness) |
|||
{ |
|||
MessageType = type; |
|||
Endianness = endianness; |
|||
if (type == MessageType.MethodCall) |
|||
ReplyExpected = true; |
|||
else |
|||
Flags = HeaderFlag.NoReplyExpected | HeaderFlag.NoAutoStart; |
|||
MajorVersion = ProtocolInformation.Version; |
|||
} |
|||
|
|||
public Header(MessageType type) : |
|||
this(type, Environment.NativeEndianness) |
|||
{} |
|||
|
|||
public EndianFlag Endianness { get; private set; } |
|||
public MessageType MessageType { get; private set; } |
|||
public HeaderFlag Flags { get; private set; } |
|||
public byte MajorVersion { get; private set; } |
|||
public uint Length { get; set; } |
|||
|
|||
public bool ReplyExpected |
|||
{ |
|||
get |
|||
{ |
|||
return (Flags & HeaderFlag.NoReplyExpected) == HeaderFlag.None; |
|||
} |
|||
set |
|||
{ |
|||
if (value) |
|||
Flags &= ~HeaderFlag.NoReplyExpected; |
|||
else |
|||
Flags |= HeaderFlag.NoReplyExpected; |
|||
} |
|||
} |
|||
|
|||
public uint Serial { get; set; } |
|||
public ObjectPath2? Path { get; set; } |
|||
public string Interface { get; set; } |
|||
public string Member { get; set; } |
|||
public string ErrorName { get; set; } |
|||
public uint? ReplySerial { get; set; } |
|||
public string Destination { get; set; } |
|||
public string Sender { get; set; } |
|||
public Signature? Signature { get; set; } |
|||
public uint NumberOfFds { get; set; } |
|||
|
|||
public static Header FromBytes(ArraySegment<byte> data) |
|||
{ |
|||
Header header = new Header(); |
|||
EndianFlag endianness = (EndianFlag)data.Array[data.Offset + 0]; |
|||
|
|||
header.Endianness = endianness; |
|||
header.MessageType = (MessageType)data.Array[data.Offset + 1]; |
|||
header.Flags = (HeaderFlag)data.Array[data.Offset + 2]; |
|||
header.MajorVersion = data.Array[data.Offset + 3]; |
|||
|
|||
var reader = new MessageReader(endianness, data); |
|||
reader.Seek(4); |
|||
header.Length = reader.ReadUInt32(); |
|||
header.Serial = reader.ReadUInt32(); |
|||
|
|||
FieldCodeEntry[] fields = reader.ReadArray<FieldCodeEntry>(); |
|||
foreach (var f in fields) |
|||
{ |
|||
var fieldCode = f.Code; |
|||
var value = f.Value; |
|||
switch (fieldCode) |
|||
{ |
|||
case FieldCode.Path: |
|||
header.Path = (ObjectPath2)value; |
|||
break; |
|||
case FieldCode.Interface: |
|||
header.Interface = (string)value; |
|||
break; |
|||
case FieldCode.Member: |
|||
header.Member = (string)value; |
|||
break; |
|||
case FieldCode.ErrorName: |
|||
header.ErrorName = (string)value; |
|||
break; |
|||
case FieldCode.ReplySerial: |
|||
header.ReplySerial = (uint)value; |
|||
break; |
|||
case FieldCode.Destination: |
|||
header.Destination = (string)value; |
|||
break; |
|||
case FieldCode.Sender: |
|||
header.Sender = (string)value; |
|||
break; |
|||
case FieldCode.Signature: |
|||
header.Signature = (Signature)value; |
|||
break; |
|||
case FieldCode.UnixFds: |
|||
header.NumberOfFds = (uint)value; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return header; |
|||
} |
|||
|
|||
public byte[] ToArray() |
|||
{ |
|||
MessageWriter writer = new MessageWriter(Endianness); |
|||
writer.WriteByte((byte)Endianness); |
|||
writer.WriteByte((byte)MessageType); |
|||
writer.WriteByte((byte)Flags); |
|||
writer.WriteByte(MajorVersion); |
|||
writer.WriteUInt32(Length); |
|||
writer.WriteUInt32(Serial); |
|||
writer.WriteHeaderFields(GetFields()); |
|||
writer.CloseWrite(); |
|||
return writer.ToArray(); |
|||
} |
|||
|
|||
public IEnumerable<KeyValuePair<FieldCode, object>> GetFields() |
|||
{ |
|||
if (Path != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Path, Path.Value); |
|||
} |
|||
if (Interface != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Interface, Interface); |
|||
} |
|||
if (Member != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Member, Member); |
|||
} |
|||
if (ErrorName != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.ErrorName, ErrorName); |
|||
} |
|||
if (ReplySerial != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.ReplySerial, ReplySerial); |
|||
} |
|||
if (Destination != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Destination, Destination); |
|||
} |
|||
if (Sender != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Sender, Sender); |
|||
} |
|||
if (Signature != null) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.Signature, Signature.Value); |
|||
} |
|||
if (NumberOfFds != 0) |
|||
{ |
|||
yield return new KeyValuePair<FieldCode, object>(FieldCode.UnixFds, NumberOfFds); |
|||
} |
|||
} |
|||
|
|||
private Header() |
|||
{ } |
|||
#pragma warning disable 0649 // Field is never assigned to, and will always have its default value
|
|||
private struct FieldCodeEntry |
|||
{ |
|||
public FieldCode Code; |
|||
public object Value; |
|||
} |
|||
#pragma warning restore
|
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
[Flags] |
|||
internal enum HeaderFlag : byte |
|||
{ |
|||
None = 0, |
|||
NoReplyExpected = 0x1, |
|||
NoAutoStart = 0x2, |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
interface IMessageStream : IDisposable |
|||
{ |
|||
Task<Message> ReceiveMessageAsync(); |
|||
Task SendMessageAsync(Message message); |
|||
void TrySendMessage(Message message); |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
interface IProxyFactory |
|||
{ |
|||
T CreateProxy<T>(string serviceName, ObjectPath2 path2); |
|||
} |
|||
} |
|||
@ -1,163 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System.Text; |
|||
using Tmds.DBus.CodeGen; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal class IntrospectionWriter |
|||
{ |
|||
private StringBuilder _sb = new StringBuilder(); |
|||
public void WriteDocType() |
|||
{ |
|||
_sb.Append("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"); |
|||
_sb.Append("\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"); |
|||
} |
|||
|
|||
public void WriteIntrospectableInterface() |
|||
{ |
|||
WriteInterfaceStart("org.freedesktop.DBus.Introspectable"); |
|||
|
|||
WriteMethodStart("Introspect"); |
|||
WriteOutArg("data", Signature.StringSig); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteInterfaceEnd(); |
|||
} |
|||
|
|||
public void WritePropertiesInterface() |
|||
{ |
|||
WriteInterfaceStart("org.freedesktop.DBus.Properties"); |
|||
|
|||
WriteMethodStart("Get"); |
|||
WriteInArg("interfaceName", Signature.StringSig); |
|||
WriteInArg("propertyName", Signature.StringSig); |
|||
WriteOutArg("value", Signature.VariantSig); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteMethodStart("Set"); |
|||
WriteInArg("interfaceName", Signature.StringSig); |
|||
WriteInArg("propertyName", Signature.StringSig); |
|||
WriteInArg("value", Signature.VariantSig); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteMethodStart("GetAll"); |
|||
WriteInArg("interfaceName", Signature.StringSig); |
|||
WriteOutArg("properties", new Signature("a{sv}")); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteSignalStart("PropertiesChanged"); |
|||
WriteArg("interfaceName", Signature.StringSig); |
|||
WriteArg("changed", new Signature("a{sv}")); |
|||
WriteArg("invalidated", new Signature("as")); |
|||
WriteSignalEnd(); |
|||
|
|||
WriteInterfaceEnd(); |
|||
} |
|||
|
|||
public void WritePeerInterface() |
|||
{ |
|||
WriteInterfaceStart("org.freedesktop.DBus.Peer"); |
|||
|
|||
WriteMethodStart("Ping"); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteMethodStart("GetMachineId"); |
|||
WriteOutArg("machineId", Signature.StringSig); |
|||
WriteMethodEnd(); |
|||
|
|||
WriteInterfaceEnd(); |
|||
} |
|||
|
|||
public void WriteInterfaceStart(string name) |
|||
{ |
|||
_sb.AppendFormat(" <interface name=\"{0}\">\n", name); |
|||
} |
|||
|
|||
public void WriteInterfaceEnd() |
|||
{ |
|||
_sb.Append(" </interface>\n"); |
|||
} |
|||
|
|||
public void WriteMethodStart(string name) |
|||
{ |
|||
_sb.AppendFormat(" <method name=\"{0}\">\n", name); |
|||
} |
|||
|
|||
public void WriteMethodEnd() |
|||
{ |
|||
_sb.Append(" </method>\n"); |
|||
} |
|||
|
|||
public void WriteInArg(string name, Signature signature) |
|||
{ |
|||
_sb.AppendFormat(" <arg direction=\"in\" name=\"{0}\" type=\"{1}\"/>\n", name, signature); |
|||
} |
|||
|
|||
public void WriteOutArg(string name, Signature signature) |
|||
{ |
|||
_sb.AppendFormat(" <arg direction=\"out\" name=\"{0}\" type=\"{1}\"/>\n", name, signature); |
|||
} |
|||
|
|||
public void WriteSignalStart(string name) |
|||
{ |
|||
_sb.AppendFormat(" <signal name=\"{0}\">\n", name); |
|||
} |
|||
|
|||
public void WriteSignalEnd() |
|||
{ |
|||
_sb.Append(" </signal>\n"); |
|||
} |
|||
|
|||
public void WriteProperty(string name, Signature signature, PropertyAccess access) |
|||
{ |
|||
string propAccess; |
|||
switch (access) |
|||
{ |
|||
case PropertyAccess.Read: |
|||
propAccess = "read"; |
|||
break; |
|||
case PropertyAccess.Write: |
|||
propAccess = "write"; |
|||
break; |
|||
case PropertyAccess.ReadWrite: |
|||
default: |
|||
propAccess = "readwrite"; |
|||
break; |
|||
} |
|||
_sb.AppendFormat(" <property name=\"{0}\" type=\"{1}\" access=\"{2}\"/>\n", name, signature, propAccess); |
|||
} |
|||
|
|||
public void WriteArg(string name, Signature signature) |
|||
{ |
|||
_sb.AppendFormat(" <arg name=\"{0}\" type=\"{1}\"/>\n", name, signature); |
|||
} |
|||
|
|||
public void WriteNodeStart(string name) |
|||
{ |
|||
_sb.AppendFormat("<node name=\"{0}\">\n", name); |
|||
} |
|||
|
|||
public void WriteNodeEnd() |
|||
{ |
|||
_sb.Append("</node>\n"); |
|||
} |
|||
|
|||
public void WriteLiteral(string value) |
|||
{ |
|||
_sb.Append(value); |
|||
} |
|||
|
|||
public void WriteChildNode(string name) |
|||
{ |
|||
_sb.AppendFormat(" <node name=\"{0}\"/>\n", name); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return _sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal class Message |
|||
{ |
|||
public Message(Header header, byte[] body, UnixFd[] unixFds) |
|||
{ |
|||
_header = header; |
|||
_body = body; |
|||
_fds = unixFds; |
|||
_header.Length = _body != null ? (uint)_body.Length : 0; |
|||
_header.NumberOfFds = (uint)(_fds?.Length ?? 0); |
|||
} |
|||
|
|||
private Header _header; |
|||
private byte[] _body; |
|||
private UnixFd[] _fds; |
|||
|
|||
public UnixFd[] UnixFds |
|||
{ |
|||
get => _fds; |
|||
set => _fds = value; |
|||
} |
|||
|
|||
public byte[] Body => _body; |
|||
|
|||
public Header Header => _header; |
|||
} |
|||
} |
|||
@ -1,656 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
#pragma warning disable 0618 // 'Marshal.SizeOf(Type)' is obsolete
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using Tmds.DBus.CodeGen; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal class MessageReader |
|||
{ |
|||
private readonly EndianFlag _endianness; |
|||
private readonly ArraySegment<byte> _data; |
|||
private readonly Message _message; |
|||
private readonly IProxyFactory _proxyFactory; |
|||
|
|||
private int _pos = 0; |
|||
private bool _skipNextStructPadding = false; |
|||
|
|||
static Dictionary<Type, bool> s_isPrimitiveStruct = new Dictionary<Type, bool> (); |
|||
|
|||
public MessageReader(EndianFlag endianness, ArraySegment<byte> data) |
|||
{ |
|||
_endianness = endianness; |
|||
_data = data; |
|||
} |
|||
|
|||
public MessageReader (Message message, IProxyFactory proxyFactory) : |
|||
this(message.Header.Endianness, new ArraySegment<byte>(message.Body ?? Array.Empty<byte>())) |
|||
{ |
|||
_message = message; |
|||
_proxyFactory = proxyFactory; |
|||
} |
|||
|
|||
public void SetSkipNextStructPadding() |
|||
{ |
|||
_skipNextStructPadding = true; |
|||
} |
|||
|
|||
public object Read(Type type) |
|||
{ |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
var value = Read(Enum.GetUnderlyingType(type)); |
|||
return Enum.ToObject(type, value); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
return ReadBoolean(); |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return ReadByte(); |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return ReadDouble(); |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return ReadInt16(); |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return ReadInt32(); |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return ReadInt64(); |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
return ReadObjectPath(); |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
return ReadSignature(); |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
return ReadString(); |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return ReadSingle(); |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return ReadUInt16(); |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return ReadUInt32(); |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return ReadUInt64(); |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
return ReadVariant(); |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
return ReadBusObject(); |
|||
} |
|||
|
|||
var method = ReadMethodFactory.CreateReadMethodForType(type); |
|||
if (method.IsStatic) |
|||
{ |
|||
return method.Invoke(null, new object[] { this }); |
|||
} |
|||
else |
|||
{ |
|||
return method.Invoke(this, null); |
|||
} |
|||
} |
|||
|
|||
public void Seek (int stride) |
|||
{ |
|||
var check = _pos + stride; |
|||
if (check < 0 || check > _data.Count) |
|||
throw new ArgumentOutOfRangeException ("stride"); |
|||
_pos = check; |
|||
} |
|||
|
|||
public T ReadDBusInterface<T>() |
|||
{ |
|||
ObjectPath2 path2 = ReadObjectPath(); |
|||
return _proxyFactory.CreateProxy<T>(_message.Header.Sender, path2); |
|||
} |
|||
|
|||
public T ReadEnum<T>() |
|||
{ |
|||
Type type = typeof(T); |
|||
var value = Read(Enum.GetUnderlyingType(type)); |
|||
return (T)Enum.ToObject(type, value); |
|||
} |
|||
|
|||
public byte ReadByte () |
|||
{ |
|||
return _data.Array[_data.Offset + _pos++]; |
|||
} |
|||
|
|||
public bool ReadBoolean () |
|||
{ |
|||
uint intval = ReadUInt32 (); |
|||
|
|||
switch (intval) { |
|||
case 0: |
|||
return false; |
|||
case 1: |
|||
return true; |
|||
default: |
|||
throw new ProtocolException("Read value " + intval + " at position " + _pos + " while expecting boolean (0/1)"); |
|||
} |
|||
} |
|||
|
|||
unsafe protected void MarshalUShort (void* dstPtr) |
|||
{ |
|||
ReadPad (2); |
|||
|
|||
if (_data.Count < _pos + 2) |
|||
throw new ProtocolException("Cannot read beyond end of data"); |
|||
|
|||
if (_endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &_data.Array[_data.Offset + _pos]) |
|||
*((ushort*)dstPtr) = *((ushort*)p); |
|||
} else { |
|||
byte* dst = (byte*)dstPtr; |
|||
dst[0] = _data.Array[_data.Offset + _pos + 1]; |
|||
dst[1] = _data.Array[_data.Offset + _pos + 0]; |
|||
} |
|||
|
|||
_pos += 2; |
|||
} |
|||
|
|||
unsafe public short ReadInt16 () |
|||
{ |
|||
short val; |
|||
|
|||
MarshalUShort (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe public ushort ReadUInt16 () |
|||
{ |
|||
ushort val; |
|||
|
|||
MarshalUShort (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe protected void MarshalUInt (void* dstPtr) |
|||
{ |
|||
ReadPad (4); |
|||
|
|||
if (_data.Count < _pos + 4) |
|||
throw new ProtocolException("Cannot read beyond end of data"); |
|||
|
|||
if (_endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &_data.Array[_data.Offset + _pos]) |
|||
*((uint*)dstPtr) = *((uint*)p); |
|||
} else { |
|||
byte* dst = (byte*)dstPtr; |
|||
dst[0] = _data.Array[_data.Offset + _pos + 3]; |
|||
dst[1] = _data.Array[_data.Offset + _pos + 2]; |
|||
dst[2] = _data.Array[_data.Offset + _pos + 1]; |
|||
dst[3] = _data.Array[_data.Offset + _pos + 0]; |
|||
} |
|||
|
|||
_pos += 4; |
|||
} |
|||
|
|||
unsafe public int ReadInt32 () |
|||
{ |
|||
int val; |
|||
|
|||
MarshalUInt (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe public uint ReadUInt32 () |
|||
{ |
|||
uint val; |
|||
|
|||
MarshalUInt (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe protected void MarshalULong (void* dstPtr) |
|||
{ |
|||
ReadPad (8); |
|||
|
|||
if (_data.Count < _pos + 8) |
|||
throw new ProtocolException("Cannot read beyond end of data"); |
|||
|
|||
if (_endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &_data.Array[_data.Offset + _pos]) |
|||
*((ulong*)dstPtr) = *((ulong*)p); |
|||
} else { |
|||
byte* dst = (byte*)dstPtr; |
|||
for (int i = 0; i < 8; ++i) |
|||
dst[i] = _data.Array[_data.Offset + _pos + (7 - i)]; |
|||
} |
|||
|
|||
_pos += 8; |
|||
} |
|||
|
|||
unsafe public long ReadInt64 () |
|||
{ |
|||
long val; |
|||
|
|||
MarshalULong (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe public ulong ReadUInt64 () |
|||
{ |
|||
ulong val; |
|||
|
|||
MarshalULong (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe public float ReadSingle () |
|||
{ |
|||
float val; |
|||
|
|||
MarshalUInt (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
unsafe public double ReadDouble () |
|||
{ |
|||
double val; |
|||
|
|||
MarshalULong (&val); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
public string ReadString () |
|||
{ |
|||
uint ln = ReadUInt32 (); |
|||
|
|||
string val = Encoding.UTF8.GetString (_data.Array, _data.Offset + _pos, (int)ln); |
|||
_pos += (int)ln; |
|||
ReadNull (); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
public void SkipString() |
|||
{ |
|||
ReadString(); |
|||
} |
|||
|
|||
public ObjectPath2 ReadObjectPath () |
|||
{ |
|||
//exactly the same as string
|
|||
return new ObjectPath2 (ReadString ()); |
|||
} |
|||
|
|||
public IDBusObject ReadBusObject() |
|||
{ |
|||
return new BusObject(ReadObjectPath()); |
|||
} |
|||
|
|||
public Signature ReadSignature () |
|||
{ |
|||
byte ln = ReadByte (); |
|||
|
|||
// Avoid an array allocation for small signatures
|
|||
if (ln == 1) { |
|||
DType dtype = (DType)ReadByte (); |
|||
ReadNull (); |
|||
return new Signature (dtype); |
|||
} |
|||
|
|||
if (ln > ProtocolInformation.MaxSignatureLength) |
|||
throw new ProtocolException("Signature length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxSignatureLength + " bytes"); |
|||
|
|||
byte[] sigData = new byte[ln]; |
|||
Array.Copy (_data.Array, _data.Offset + _pos, sigData, 0, (int)ln); |
|||
_pos += (int)ln; |
|||
ReadNull (); |
|||
|
|||
return Signature.Take (sigData); |
|||
} |
|||
|
|||
public T ReadSafeHandle<T>() |
|||
{ |
|||
var idx = ReadInt32(); |
|||
var fds = _message.UnixFds; |
|||
int fd = -1; |
|||
if (fds != null && idx < fds.Length) |
|||
{ |
|||
fd = fds[idx].Handle; |
|||
} |
|||
return (T)Activator.CreateInstance(typeof(T), new object[] { (IntPtr)fd , true }); |
|||
} |
|||
|
|||
public object ReadVariant () |
|||
{ |
|||
var sig = ReadSignature (); |
|||
if (!sig.IsSingleCompleteType) |
|||
throw new InvalidOperationException (string.Format ("ReadVariant need a single complete type signature, {0} was given", sig.ToString ())); |
|||
return Read(sig.ToType()); |
|||
} |
|||
|
|||
public T ReadVariantAsType<T>() |
|||
{ |
|||
var sig = ReadSignature (); |
|||
if (!sig.IsSingleCompleteType) |
|||
throw new InvalidOperationException (string.Format ("ReadVariant need a single complete type signature, {0} was given", sig.ToString ())); |
|||
var type = typeof(T); |
|||
if (sig != Signature.GetSig(type, isCompileTimeType: true)) |
|||
throw new InvalidCastException($"Cannot convert dbus type '{sig.ToString()}' to '{type.FullName}'"); |
|||
return (T)Read(type); |
|||
} |
|||
|
|||
public T ReadDictionaryObject<T>() |
|||
{ |
|||
var type = typeof(T); |
|||
FieldInfo[] fis = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); |
|||
|
|||
object val = Activator.CreateInstance (type); |
|||
|
|||
uint ln = ReadUInt32 (); |
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
ReadPad (8); |
|||
|
|||
int endPos = _pos + (int)ln; |
|||
|
|||
while (_pos < endPos) { |
|||
ReadPad (8); |
|||
|
|||
var key = ReadString(); |
|||
var sig = ReadSignature(); |
|||
|
|||
if (!sig.IsSingleCompleteType) |
|||
throw new InvalidOperationException (string.Format ("ReadVariant need a single complete type signature, {0} was given", sig.ToString ())); |
|||
|
|||
// if the key contains a '-' which is an invalid identifier character,
|
|||
// we try and replace it with '_' and see if we find a match.
|
|||
// The name may be prefixed with '_'.
|
|||
var field = fis.Where(f => ((f.Name.Length == key.Length) || (f.Name.Length == key.Length + 1 && f.Name[0] == '_')) |
|||
&& (f.Name.EndsWith(key, StringComparison.Ordinal) || (key.Contains("-") && f.Name.Replace('_', '-').EndsWith(key, StringComparison.Ordinal)))).SingleOrDefault(); |
|||
|
|||
if (field == null) |
|||
{ |
|||
var value = Read(sig.ToType()); |
|||
} |
|||
else |
|||
{ |
|||
Type fieldType; |
|||
string propertyName; |
|||
PropertyTypeInspector.InspectField(field, out propertyName, out fieldType); |
|||
|
|||
if (sig != Signature.GetSig(fieldType, isCompileTimeType: true)) |
|||
{ |
|||
throw new ArgumentException($"Dictionary '{type.FullName}' field '{field.Name}' with type '{fieldType.FullName}' cannot be read from D-Bus type '{sig}'"); |
|||
} |
|||
|
|||
var readValue = Read(fieldType); |
|||
|
|||
field.SetValue (val, readValue); |
|||
} |
|||
} |
|||
|
|||
if (_pos != endPos) |
|||
throw new ProtocolException("Read pos " + _pos + " != ep " + endPos); |
|||
|
|||
return (T)val; |
|||
} |
|||
|
|||
public Dictionary<TKey, TValue> ReadDictionary<TKey, TValue> () |
|||
{ |
|||
uint ln = ReadUInt32 (); |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
var val = new Dictionary<TKey, TValue> ((int)(ln / 8)); |
|||
ReadPad (8); |
|||
|
|||
int endPos = _pos + (int)ln; |
|||
|
|||
var keyReader = ReadMethodFactory.CreateReadMethodDelegate<TKey>(); |
|||
var valueReader = ReadMethodFactory.CreateReadMethodDelegate<TValue>(); |
|||
|
|||
while (_pos < endPos) { |
|||
ReadPad (8); |
|||
TKey k = keyReader(this); |
|||
TValue v = valueReader(this); |
|||
val.Add (k, v); |
|||
} |
|||
|
|||
if (_pos != endPos) |
|||
throw new ProtocolException("Read pos " + _pos + " != ep " + endPos); |
|||
|
|||
return val; |
|||
} |
|||
|
|||
public T[] ReadArray<T> () |
|||
{ |
|||
uint ln = ReadUInt32 (); |
|||
Type elemType = typeof (T); |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Array length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
//advance to the alignment of the element
|
|||
ReadPad (ProtocolInformation.GetAlignment (elemType)); |
|||
|
|||
if (elemType.GetTypeInfo().IsPrimitive) { |
|||
// Fast path for primitive types (except bool which isn't blittable and take another path)
|
|||
if (elemType != typeof (bool)) |
|||
return MarshalArray<T> (ln); |
|||
else |
|||
return (T[])(Array)MarshalBoolArray (ln); |
|||
} |
|||
|
|||
var list = new List<T> (); |
|||
int endPos = _pos + (int)ln; |
|||
|
|||
var elementReader = ReadMethodFactory.CreateReadMethodDelegate<T>(); |
|||
|
|||
while (_pos < endPos) |
|||
list.Add (elementReader(this)); |
|||
|
|||
if (_pos != endPos) |
|||
throw new ProtocolException("Read pos " + _pos + " != ep " + endPos); |
|||
|
|||
return list.ToArray (); |
|||
} |
|||
|
|||
TArray[] MarshalArray<TArray> (uint length) |
|||
{ |
|||
int sof = Marshal.SizeOf<TArray>(); |
|||
TArray[] array = new TArray[(int)(length / sof)]; |
|||
|
|||
if (_endianness == Environment.NativeEndianness) { |
|||
Buffer.BlockCopy (_data.Array, _data.Offset + _pos, array, 0, (int)length); |
|||
_pos += (int)length; |
|||
} else { |
|||
GCHandle handle = GCHandle.Alloc (array, GCHandleType.Pinned); |
|||
DirectCopy (sof, array.Length, handle); |
|||
handle.Free (); |
|||
} |
|||
|
|||
return array; |
|||
} |
|||
|
|||
void DirectCopy (int sof, int length, GCHandle handle) |
|||
{ |
|||
DirectCopy (sof, length, handle.AddrOfPinnedObject ()); |
|||
} |
|||
|
|||
unsafe void DirectCopy (int sof, int length, IntPtr handle) |
|||
{ |
|||
int byteLength = length * sof; |
|||
if (_endianness == Environment.NativeEndianness) { |
|||
Marshal.Copy (_data.Array, _data.Offset + _pos, handle, byteLength); |
|||
} else { |
|||
byte* ptr = (byte*)(void*)handle; |
|||
for (int i = _pos; i < _pos + byteLength; i += sof) |
|||
for (int j = i; j < i + sof; j++) |
|||
ptr[2 * i - _pos + (sof - 1) - j] = _data.Array[_data.Offset + j]; |
|||
} |
|||
|
|||
_pos += byteLength; |
|||
} |
|||
|
|||
bool[] MarshalBoolArray (uint length) |
|||
{ |
|||
bool[] array = new bool [length]; |
|||
for (int i = 0; i < length; i++) |
|||
array[i] = ReadBoolean (); |
|||
|
|||
return array; |
|||
} |
|||
|
|||
public T ReadStruct<T> () |
|||
{ |
|||
if (!_skipNextStructPadding) |
|||
{ |
|||
ReadPad (8); |
|||
} |
|||
_skipNextStructPadding = false; |
|||
|
|||
FieldInfo[] fis = ArgTypeInspector.GetStructFields(typeof(T), isValueTuple: false); |
|||
|
|||
// Empty struct? No need for processing
|
|||
if (fis.Length == 0) |
|||
return default (T); |
|||
|
|||
if (IsEligibleStruct (typeof(T), fis)) |
|||
return (T)MarshalStruct (typeof(T), fis); |
|||
|
|||
object val = Activator.CreateInstance<T> (); |
|||
|
|||
foreach (System.Reflection.FieldInfo fi in fis) |
|||
fi.SetValue (val, Read (fi.FieldType)); |
|||
|
|||
return (T)val; |
|||
} |
|||
|
|||
public T ReadValueTupleStruct<T> () |
|||
{ |
|||
if (!_skipNextStructPadding) |
|||
{ |
|||
ReadPad (8); |
|||
} |
|||
_skipNextStructPadding = false; |
|||
|
|||
bool isValueTuple = true; |
|||
FieldInfo[] fis = ArgTypeInspector.GetStructFields(typeof(T), isValueTuple); |
|||
|
|||
// Empty struct? No need for processing
|
|||
if (fis.Length == 0) |
|||
return default (T); |
|||
|
|||
object val = Activator.CreateInstance<T> (); |
|||
|
|||
for (int i = 0; i < fis.Length; i++) |
|||
{ |
|||
var fi = fis[i]; |
|||
if (i == 7 && isValueTuple) |
|||
{ |
|||
_skipNextStructPadding = true; |
|||
} |
|||
fi.SetValue (val, Read(fi.FieldType)); |
|||
} |
|||
|
|||
return (T)val; |
|||
} |
|||
|
|||
object MarshalStruct (Type structType, FieldInfo[] fis) |
|||
{ |
|||
object strct = Activator.CreateInstance (structType); |
|||
int sof = Marshal.SizeOf (fis[0].FieldType); |
|||
GCHandle handle = GCHandle.Alloc (strct, GCHandleType.Pinned); |
|||
DirectCopy (sof, fis.Length, handle); |
|||
handle.Free (); |
|||
|
|||
return strct; |
|||
} |
|||
|
|||
public void ReadNull () |
|||
{ |
|||
if (_data.Array[_data.Offset + _pos] != 0) |
|||
throw new ProtocolException("Read non-zero byte at position " + _pos + " while expecting null terminator"); |
|||
_pos++; |
|||
} |
|||
|
|||
public void ReadPad (int alignment) |
|||
{ |
|||
for (int endPos = ProtocolInformation.Padded (_pos, alignment) ; _pos != endPos ; _pos++) |
|||
if (_data.Array[_data.Offset + _pos] != 0) |
|||
throw new ProtocolException("Read non-zero byte at position " + _pos + " while expecting padding. Value given: " + _data.Array[_data.Offset + _pos]); |
|||
} |
|||
|
|||
// If a struct is only composed of primitive type fields (i.e. blittable types)
|
|||
// then this method return true. Result is cached in isPrimitiveStruct dictionary.
|
|||
internal static bool IsEligibleStruct (Type structType, FieldInfo[] fields) |
|||
{ |
|||
lock (s_isPrimitiveStruct) |
|||
{ |
|||
bool result; |
|||
if (s_isPrimitiveStruct.TryGetValue(structType, out result)) |
|||
return result; |
|||
|
|||
var typeInfo = structType.GetTypeInfo(); |
|||
if (!typeInfo.IsLayoutSequential) |
|||
return false; |
|||
|
|||
if (!(s_isPrimitiveStruct[structType] = fields.All((f) => f.FieldType.GetTypeInfo().IsPrimitive && f.FieldType != typeof(bool)))) |
|||
return false; |
|||
|
|||
int alignement = ProtocolInformation.GetAlignment(fields[0].FieldType); |
|||
|
|||
return s_isPrimitiveStruct[structType] = !fields.Any((f) => ProtocolInformation.GetAlignment(f.FieldType) != alignement); |
|||
} |
|||
} |
|||
|
|||
private class BusObject : IDBusObject |
|||
{ |
|||
public BusObject(ObjectPath2 path2) |
|||
{ |
|||
ObjectPath2 = path2; |
|||
} |
|||
|
|||
public ObjectPath2 ObjectPath2 { get; } |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2010 Alan McGovern <alan.mcgovern@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal enum MessageType : byte |
|||
{ |
|||
//This is an invalid type.
|
|||
Invalid, |
|||
//Method call.
|
|||
MethodCall, |
|||
//Method reply with returned data.
|
|||
MethodReturn, |
|||
//Error reply. If the first argument exists and is a string, it is an error message.
|
|||
Error, |
|||
//Signal emission.
|
|||
Signal, |
|||
All |
|||
// Correspond to all types
|
|||
} |
|||
} |
|||
@ -1,608 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
#pragma warning disable 0618 // 'Marshal.SizeOf(Type)' is obsolete
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using System.Linq; |
|||
using Tmds.DBus.CodeGen; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal sealed class MessageWriter |
|||
{ |
|||
EndianFlag endianness; |
|||
MemoryStream stream; |
|||
List<UnixFd> fds; |
|||
bool _skipNextStructPadding; |
|||
|
|||
static readonly Encoding stringEncoding = Encoding.UTF8; |
|||
|
|||
//a default constructor is a bad idea for now as we want to make sure the header and content-type match
|
|||
public MessageWriter () : this (Environment.NativeEndianness) {} |
|||
|
|||
public MessageWriter (EndianFlag endianness) |
|||
{ |
|||
this.endianness = endianness; |
|||
stream = new MemoryStream (); |
|||
} |
|||
|
|||
public void SetSkipNextStructPadding() |
|||
{ |
|||
_skipNextStructPadding = true; |
|||
} |
|||
|
|||
public byte[] ToArray () |
|||
{ |
|||
return stream.ToArray (); |
|||
} |
|||
|
|||
public UnixFd[] UnixFds => fds?.ToArray() ?? Array.Empty<UnixFd>(); |
|||
|
|||
public void CloseWrite () |
|||
{ |
|||
WritePad (8); |
|||
} |
|||
|
|||
public void WriteByte (byte val) |
|||
{ |
|||
stream.WriteByte (val); |
|||
} |
|||
|
|||
public void WriteBoolean (bool val) |
|||
{ |
|||
WriteUInt32 ((uint) (val ? 1 : 0)); |
|||
} |
|||
|
|||
// Buffer for integer marshaling
|
|||
byte[] dst = new byte[8]; |
|||
private unsafe void MarshalUShort (void* dataPtr) |
|||
{ |
|||
WritePad (2); |
|||
|
|||
if (endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &dst[0]) |
|||
*((ushort*)p) = *((ushort*)dataPtr); |
|||
} else { |
|||
byte* data = (byte*)dataPtr; |
|||
dst[0] = data[1]; |
|||
dst[1] = data[0]; |
|||
} |
|||
|
|||
stream.Write (dst, 0, 2); |
|||
} |
|||
|
|||
unsafe public void WriteInt16 (short val) |
|||
{ |
|||
MarshalUShort (&val); |
|||
} |
|||
|
|||
unsafe public void WriteUInt16 (ushort val) |
|||
{ |
|||
MarshalUShort (&val); |
|||
} |
|||
|
|||
private unsafe void MarshalUInt (void* dataPtr) |
|||
{ |
|||
WritePad (4); |
|||
|
|||
if (endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &dst[0]) |
|||
*((uint*)p) = *((uint*)dataPtr); |
|||
} else { |
|||
byte* data = (byte*)dataPtr; |
|||
dst[0] = data[3]; |
|||
dst[1] = data[2]; |
|||
dst[2] = data[1]; |
|||
dst[3] = data[0]; |
|||
} |
|||
|
|||
stream.Write (dst, 0, 4); |
|||
} |
|||
|
|||
unsafe public void WriteInt32 (int val) |
|||
{ |
|||
MarshalUInt (&val); |
|||
} |
|||
|
|||
unsafe public void WriteUInt32 (uint val) |
|||
{ |
|||
MarshalUInt (&val); |
|||
} |
|||
|
|||
private unsafe void MarshalULong (void* dataPtr) |
|||
{ |
|||
WritePad (8); |
|||
|
|||
if (endianness == Environment.NativeEndianness) { |
|||
fixed (byte* p = &dst[0]) |
|||
*((ulong*)p) = *((ulong*)dataPtr); |
|||
} else { |
|||
byte* data = (byte*)dataPtr; |
|||
for (int i = 0; i < 8; ++i) |
|||
dst[i] = data[7 - i]; |
|||
} |
|||
|
|||
stream.Write (dst, 0, 8); |
|||
} |
|||
|
|||
unsafe public void WriteInt64 (long val) |
|||
{ |
|||
MarshalULong (&val); |
|||
} |
|||
|
|||
unsafe public void WriteUInt64 (ulong val) |
|||
{ |
|||
MarshalULong (&val); |
|||
} |
|||
|
|||
unsafe public void WriteSingle (float val) |
|||
{ |
|||
MarshalUInt (&val); |
|||
} |
|||
|
|||
unsafe public void WriteDouble (double val) |
|||
{ |
|||
MarshalULong (&val); |
|||
} |
|||
|
|||
public void WriteString (string val) |
|||
{ |
|||
byte[] utf8_data = stringEncoding.GetBytes (val); |
|||
WriteUInt32 ((uint)utf8_data.Length); |
|||
stream.Write (utf8_data, 0, utf8_data.Length); |
|||
WriteNull (); |
|||
} |
|||
|
|||
public void WriteObjectPath (ObjectPath2 val) |
|||
{ |
|||
WriteString (val.Value); |
|||
} |
|||
|
|||
public void WriteSignature(Signature val) |
|||
{ |
|||
byte[] ascii_data = val.GetBuffer (); |
|||
|
|||
if (ascii_data.Length > ProtocolInformation.MaxSignatureLength) |
|||
throw new ProtocolException("Signature length " + ascii_data.Length + " exceeds maximum allowed " + ProtocolInformation.MaxSignatureLength + " bytes"); |
|||
|
|||
WriteByte ((byte)ascii_data.Length); |
|||
stream.Write (ascii_data, 0, ascii_data.Length); |
|||
WriteNull (); |
|||
} |
|||
|
|||
public void WriteSafeHandle(SafeHandle handle) |
|||
{ |
|||
if (fds == null) |
|||
{ |
|||
fds = new List<UnixFd>(); |
|||
} |
|||
fds.Add(new UnixFd(handle)); |
|||
WriteInt32(fds.Count - 1); |
|||
} |
|||
|
|||
public void Write(Type type, object val, bool isCompileTimeType) |
|||
{ |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
type = Enum.GetUnderlyingType(type); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
WriteBoolean((bool)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
WriteByte((byte)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
WriteDouble((double)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
WriteInt16((short)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
WriteInt32((int)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
WriteInt64((long)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
WriteObjectPath((ObjectPath2)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
WriteSignature((Signature)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
WriteString((string)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
WriteSingle((float)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
WriteUInt16((ushort)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
WriteUInt32((uint)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
WriteUInt64((ulong)val); |
|||
return; |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
WriteVariant(val); |
|||
return; |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
WriteBusObject((IDBusObject)val); |
|||
return; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType)) |
|||
{ |
|||
WriteBusObject((IDBusObject)val); |
|||
return; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsSafeHandleType(type)) |
|||
{ |
|||
WriteSafeHandle((SafeHandle)val); |
|||
return; |
|||
} |
|||
|
|||
MethodInfo method = WriteMethodFactory.CreateWriteMethodForType(type, isCompileTimeType); |
|||
|
|||
if (method.IsStatic) |
|||
{ |
|||
method.Invoke(null, new object[] { this, val }); |
|||
} |
|||
else |
|||
{ |
|||
method.Invoke(this, new object[] { val }); |
|||
} |
|||
} |
|||
|
|||
private void WriteObject (Type type, object val) |
|||
{ |
|||
ObjectPath2 path2; |
|||
|
|||
DBusObjectProxy bobj = val as DBusObjectProxy; |
|||
|
|||
if (bobj == null) |
|||
throw new ArgumentException("No object reference to write", nameof(val)); |
|||
|
|||
path2 = bobj.ObjectPath2; |
|||
|
|||
WriteObjectPath (path2); |
|||
} |
|||
|
|||
public void WriteBusObject(IDBusObject busObject) |
|||
{ |
|||
WriteObjectPath(busObject.ObjectPath2); |
|||
} |
|||
|
|||
public void WriteVariant (object val) |
|||
{ |
|||
if (val == null) |
|||
throw new NotSupportedException ("Cannot send null variant"); |
|||
|
|||
Type type = val.GetType (); |
|||
|
|||
if (type == typeof(object)) |
|||
{ |
|||
throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'"); |
|||
} |
|||
|
|||
Signature sig = Signature.GetSig(type, isCompileTimeType: false); |
|||
|
|||
WriteSignature(sig); |
|||
Write(type, val, isCompileTimeType: false); |
|||
} |
|||
|
|||
public void WriteArray<T> (IEnumerable<T> val) |
|||
{ |
|||
Type elemType = typeof (T); |
|||
|
|||
var byteArray = val as byte[]; |
|||
if (byteArray != null) { |
|||
int valLength = val.Count(); |
|||
if (byteArray.Length > ProtocolInformation.MaxArrayLength) |
|||
ThrowArrayLengthException ((uint)byteArray.Length); |
|||
|
|||
WriteUInt32 ((uint)byteArray.Length); |
|||
stream.Write (byteArray, 0, byteArray.Length); |
|||
return; |
|||
} |
|||
|
|||
if (elemType.GetTypeInfo().IsEnum) |
|||
elemType = Enum.GetUnderlyingType (elemType); |
|||
|
|||
Signature sigElem = Signature.GetSig (elemType, isCompileTimeType: true); |
|||
int fixedSize = 0; |
|||
|
|||
if (endianness == Environment.NativeEndianness && elemType.GetTypeInfo().IsValueType && !sigElem.IsStruct && elemType != typeof(bool) && |
|||
sigElem.GetFixedSize (ref fixedSize) && val is Array) { |
|||
var array = val as Array; |
|||
int byteLength = fixedSize * array.Length; |
|||
if (byteLength > ProtocolInformation.MaxArrayLength) |
|||
ThrowArrayLengthException ((uint)byteLength); |
|||
|
|||
WriteUInt32 ((uint)byteLength); |
|||
WritePad (sigElem.Alignment); |
|||
|
|||
byte[] data = new byte[byteLength]; |
|||
Buffer.BlockCopy (array, 0, data, 0, data.Length); |
|||
stream.Write (data, 0, data.Length); |
|||
|
|||
return; |
|||
} |
|||
|
|||
long origPos = stream.Position; |
|||
WriteUInt32 ((uint)0); |
|||
|
|||
WritePad (sigElem.Alignment); |
|||
|
|||
long startPos = stream.Position; |
|||
|
|||
var tWriter = WriteMethodFactory.CreateWriteMethodDelegate<T>(); |
|||
|
|||
foreach (T elem in val) |
|||
tWriter (this, elem); |
|||
|
|||
long endPos = stream.Position; |
|||
uint ln = (uint)(endPos - startPos); |
|||
stream.Position = origPos; |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
ThrowArrayLengthException (ln); |
|||
|
|||
WriteUInt32 (ln); |
|||
stream.Position = endPos; |
|||
} |
|||
|
|||
internal static void ThrowArrayLengthException (uint ln) |
|||
{ |
|||
throw new ProtocolException("Array length " + ln.ToString () + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
} |
|||
|
|||
public void WriteStructure<T> (T value) |
|||
{ |
|||
|
|||
if (!_skipNextStructPadding) |
|||
{ |
|||
WritePad (8); |
|||
} |
|||
_skipNextStructPadding = false; |
|||
|
|||
FieldInfo[] fis = ArgTypeInspector.GetStructFields(typeof(T), isValueTuple: false); |
|||
if (fis.Length == 0) |
|||
return; |
|||
|
|||
object boxed = value; |
|||
|
|||
if (MessageReader.IsEligibleStruct (typeof (T), fis)) { |
|||
byte[] buffer = new byte[Marshal.SizeOf (fis[0].FieldType) * fis.Length]; |
|||
|
|||
unsafe { |
|||
GCHandle valueHandle = GCHandle.Alloc (boxed, GCHandleType.Pinned); |
|||
Marshal.Copy (valueHandle.AddrOfPinnedObject (), buffer, 0, buffer.Length); |
|||
valueHandle.Free (); |
|||
} |
|||
stream.Write (buffer, 0, buffer.Length); |
|||
return; |
|||
} |
|||
|
|||
foreach (var fi in fis) |
|||
Write (fi.FieldType, fi.GetValue (boxed), isCompileTimeType: true); |
|||
} |
|||
|
|||
public void WriteValueTupleStructure<T> (T value) |
|||
{ |
|||
if (!_skipNextStructPadding) |
|||
{ |
|||
WritePad (8); |
|||
} |
|||
_skipNextStructPadding = false; |
|||
FieldInfo[] fis = ArgTypeInspector.GetStructFields(typeof(T), isValueTuple: true); |
|||
if (fis.Length == 0) |
|||
return; |
|||
|
|||
object boxed = value; |
|||
for (int i = 0; i < fis.Length;) |
|||
{ |
|||
var fi = fis[i]; |
|||
if (i == 7) |
|||
{ |
|||
boxed = fi.GetValue (boxed); |
|||
fis = ArgTypeInspector.GetStructFields(fi.FieldType, isValueTuple: true); |
|||
i = 0; |
|||
} |
|||
else |
|||
{ |
|||
Write (fi.FieldType, fi.GetValue (boxed), isCompileTimeType: true); |
|||
i++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void WriteFromDict<TKey,TValue> (IEnumerable<KeyValuePair<TKey,TValue>> val) |
|||
{ |
|||
long origPos = stream.Position; |
|||
// Pre-write array length field, we overwrite it at the end with the correct value
|
|||
WriteUInt32 ((uint)0); |
|||
WritePad (8); |
|||
long startPos = stream.Position; |
|||
|
|||
var keyWriter = WriteMethodFactory.CreateWriteMethodDelegate<TKey>(); |
|||
var valueWriter = WriteMethodFactory.CreateWriteMethodDelegate<TValue>(); |
|||
|
|||
foreach (KeyValuePair<TKey,TValue> entry in val) { |
|||
WritePad (8); |
|||
keyWriter (this, entry.Key); |
|||
valueWriter (this, entry.Value); |
|||
} |
|||
|
|||
long endPos = stream.Position; |
|||
uint ln = (uint)(endPos - startPos); |
|||
stream.Position = origPos; |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
WriteUInt32 (ln); |
|||
stream.Position = endPos; |
|||
} |
|||
|
|||
public void WriteDictionaryObject<T>(T val) |
|||
{ |
|||
var type = typeof(T); |
|||
FieldInfo[] fis = type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); |
|||
|
|||
long origPos = stream.Position; |
|||
// Pre-write array length field, we overwrite it at the end with the correct value
|
|||
WriteUInt32 ((uint)0); |
|||
WritePad (8); |
|||
long startPos = stream.Position; |
|||
|
|||
foreach (var fi in fis) |
|||
{ |
|||
object fieldVal = fi.GetValue(val); |
|||
if (fieldVal == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
Type fieldType; |
|||
string fieldName; |
|||
PropertyTypeInspector.InspectField(fi, out fieldName, out fieldType); |
|||
Signature sig = Signature.GetSig(fieldType, isCompileTimeType: true); |
|||
|
|||
WritePad (8); |
|||
WriteString(fieldName); |
|||
WriteSignature(sig); |
|||
Write(fieldType, fieldVal, isCompileTimeType: true); |
|||
} |
|||
|
|||
long endPos = stream.Position; |
|||
uint ln = (uint)(endPos - startPos); |
|||
stream.Position = origPos; |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
WriteUInt32 (ln); |
|||
stream.Position = endPos; |
|||
} |
|||
|
|||
public void WriteHeader(Header header) |
|||
{ |
|||
WriteByte((byte)header.Endianness); |
|||
WriteByte((byte)header.MessageType); |
|||
WriteByte((byte)header.Flags); |
|||
WriteByte(header.MajorVersion); |
|||
WriteUInt32(header.Length); |
|||
WriteUInt32(header.Serial); |
|||
WriteHeaderFields(header.GetFields()); |
|||
CloseWrite(); |
|||
} |
|||
|
|||
internal void WriteHeaderFields(IEnumerable<KeyValuePair<FieldCode, object>> val) |
|||
{ |
|||
long origPos = stream.Position; |
|||
WriteUInt32 ((uint)0); |
|||
|
|||
WritePad (8); |
|||
|
|||
long startPos = stream.Position; |
|||
|
|||
foreach (KeyValuePair<FieldCode, object> entry in val) { |
|||
WritePad (8); |
|||
WriteByte ((byte)entry.Key); |
|||
switch (entry.Key) { |
|||
case FieldCode.Destination: |
|||
case FieldCode.ErrorName: |
|||
case FieldCode.Interface: |
|||
case FieldCode.Member: |
|||
case FieldCode.Sender: |
|||
WriteSignature (Signature.StringSig); |
|||
WriteString((string)entry.Value); |
|||
break; |
|||
case FieldCode.Path: |
|||
WriteSignature(Signature.ObjectPathSig); |
|||
WriteObjectPath((ObjectPath2)entry.Value); |
|||
break; |
|||
case FieldCode.ReplySerial: |
|||
WriteSignature(Signature.UInt32Sig); |
|||
WriteUInt32((uint)entry.Value); |
|||
break; |
|||
case FieldCode.Signature: |
|||
WriteSignature(Signature.SignatureSig); |
|||
Signature sig = (Signature)entry.Value; |
|||
WriteSignature((Signature)entry.Value); |
|||
break; |
|||
default: |
|||
WriteVariant (entry.Value); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
long endPos = stream.Position; |
|||
uint ln = (uint)(endPos - startPos); |
|||
stream.Position = origPos; |
|||
|
|||
if (ln > ProtocolInformation.MaxArrayLength) |
|||
throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); |
|||
|
|||
WriteUInt32 (ln); |
|||
stream.Position = endPos; |
|||
} |
|||
|
|||
private void WriteNull () |
|||
{ |
|||
stream.WriteByte (0); |
|||
} |
|||
|
|||
// Source buffer for zero-padding
|
|||
static readonly byte[] nullBytes = new byte[8]; |
|||
private void WritePad (int alignment) |
|||
{ |
|||
int needed = ProtocolInformation.PadNeeded ((int)stream.Position, alignment); |
|||
if (needed == 0) |
|||
return; |
|||
stream.Write (nullBytes, 0, needed); |
|||
} |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
delegate Task<Message> MethodHandler(Message message); |
|||
} |
|||
@ -1,177 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Tmds.DBus.CodeGen; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal static class ProtocolInformation |
|||
{ |
|||
//protocol versions
|
|||
public const byte Version = 1; |
|||
|
|||
public const uint MaxMessageLength = 134217728; //2 to the 27th power
|
|||
public const uint MaxArrayLength = 67108864; //2 to the 26th power
|
|||
public const uint MaxSignatureLength = 255; |
|||
public const uint MaxArrayDepth = 32; |
|||
public const uint MaxStructDepth = 32; |
|||
|
|||
//this is not strictly related to Protocol since names are passed around as strings
|
|||
internal const uint MaxNameLength = 255; |
|||
internal const uint MaxMatchRuleLength = 1024; |
|||
internal const uint MaxMatchRuleArgs = 64; |
|||
|
|||
public static int PadNeeded (int pos, int alignment) |
|||
{ |
|||
int pad = pos % alignment; |
|||
return pad == 0 ? 0 : alignment - pad; |
|||
} |
|||
|
|||
public static int Padded (int pos, int alignment) |
|||
{ |
|||
int pad = pos % alignment; |
|||
if (pad != 0) |
|||
pos += alignment - pad; |
|||
|
|||
return pos; |
|||
} |
|||
|
|||
public static int GetAlignment(Type type) |
|||
{ |
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
type = Enum.GetUnderlyingType(type); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
return GetAlignment(DType.Boolean); |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return GetAlignment(DType.Byte); |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return GetAlignment(DType.Double); |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return GetAlignment(DType.Int16); |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return GetAlignment(DType.Int32); |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return GetAlignment(DType.Int64); |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
return GetAlignment(DType.ObjectPath); |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
return GetAlignment(DType.Signature); |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
return GetAlignment(DType.String); |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return GetAlignment(DType.Single); |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return GetAlignment(DType.UInt16); |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return GetAlignment(DType.UInt32); |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return GetAlignment(DType.UInt64); |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
return GetAlignment(DType.Variant); |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
return GetAlignment(DType.Variant); |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType: true)) |
|||
{ |
|||
return GetAlignment(DType.Variant); |
|||
} |
|||
|
|||
Type elementType; |
|||
if (ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType: true) |
|||
!= ArgTypeInspector.EnumerableType.NotEnumerable) |
|||
{ |
|||
return GetAlignment(DType.Array); |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsStructType(type)) |
|||
{ |
|||
return GetAlignment(DType.StructBegin); |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsSafeHandleType(type)) |
|||
{ |
|||
return GetAlignment(DType.UnixFd); |
|||
} |
|||
|
|||
throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'"); |
|||
} |
|||
|
|||
public static int GetAlignment (DType dtype) |
|||
{ |
|||
switch (dtype) { |
|||
case DType.Byte: |
|||
return 1; |
|||
case DType.Boolean: |
|||
return 4; |
|||
case DType.Int16: |
|||
case DType.UInt16: |
|||
return 2; |
|||
case DType.Int32: |
|||
case DType.UInt32: |
|||
return 4; |
|||
case DType.Int64: |
|||
case DType.UInt64: |
|||
return 8; |
|||
case DType.Single: //Not yet supported!
|
|||
return 4; |
|||
case DType.Double: |
|||
return 8; |
|||
case DType.String: |
|||
return 4; |
|||
case DType.ObjectPath: |
|||
return 4; |
|||
case DType.Signature: |
|||
return 1; |
|||
case DType.Array: |
|||
return 4; |
|||
case DType.StructBegin: |
|||
return 8; |
|||
case DType.Variant: |
|||
return 1; |
|||
case DType.DictEntryBegin: |
|||
return 8; |
|||
case DType.Invalid: |
|||
default: |
|||
throw new ProtocolException("Cannot determine alignment of " + dtype); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
delegate void SignalHandler(Message message, Exception ex); |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Tmds.DBus.Protocol |
|||
{ |
|||
internal struct UnixFd |
|||
{ |
|||
public SafeHandle SafeHandle { get; private set; } |
|||
public int Handle { get; private set; } |
|||
|
|||
public UnixFd(SafeHandle handle) |
|||
{ |
|||
SafeHandle = handle; |
|||
Handle = SafeHandle.DangerousGetHandle().ToInt32(); |
|||
} |
|||
|
|||
public UnixFd(int handle) |
|||
{ |
|||
SafeHandle = null; |
|||
Handle = handle; |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Exception thrown when there is an error in the D-Bus protocol.
|
|||
/// </summary>
|
|||
public class ProtocolException : Exception |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an instance of the ProtocolException with the specified message.
|
|||
/// </summary>
|
|||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
|||
public ProtocolException(string message) : base(message) |
|||
{} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal enum ReleaseNameReply : uint |
|||
{ |
|||
ReplyReleased = 1, |
|||
NonExistent, |
|||
NotOwner |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
[Flags] |
|||
internal enum RequestNameOptions : uint |
|||
{ |
|||
None = 0, |
|||
AllowReplacement = 0x1, |
|||
ReplaceExisting = 0x2, |
|||
DoNotQueue = 0x4, |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal enum RequestNameReply : uint |
|||
{ |
|||
PrimaryOwner = 1, |
|||
InQueue, |
|||
Exists, |
|||
AlreadyOwner, |
|||
} |
|||
} |
|||
@ -1,63 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Net; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Options that configure the behavior of a Connection for a D-Bus local server.
|
|||
/// </summary>
|
|||
public class ServerConnectionOptions : ConnectionOptions |
|||
{ |
|||
private Connection2 _connection2; |
|||
|
|||
/// <summary>
|
|||
/// Starts the server at the specified address.
|
|||
/// </summary>
|
|||
/// <param name="address">Address of the D-Bus peer.</param>
|
|||
/// <returns>
|
|||
/// Bound address.
|
|||
/// </returns>
|
|||
public Task<string> StartAsync(string address) |
|||
=> StartAsync(new ServerStartOptions { Address = address }); |
|||
|
|||
/// <summary>
|
|||
/// Starts the server with the specified options.
|
|||
/// </summary>
|
|||
/// <param name="options"></param>
|
|||
/// <returns>
|
|||
/// Bound address.
|
|||
/// </returns>
|
|||
public Task<string> StartAsync(ServerStartOptions options) |
|||
{ |
|||
if (_connection2 == null) |
|||
{ |
|||
throw new InvalidOperationException("Not attached to connection."); |
|||
} |
|||
|
|||
return _connection2.StartServerAsync(options.Address); |
|||
} |
|||
|
|||
internal Connection2 Connection2 |
|||
{ |
|||
get => _connection2; |
|||
set |
|||
{ |
|||
if (_connection2 != null) |
|||
{ |
|||
throw new InvalidOperationException("Already attached to another connection."); |
|||
} |
|||
if (value == null) |
|||
{ |
|||
throw new ArgumentNullException(); |
|||
} |
|||
_connection2 = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Net; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Options that configure the behavior of ServerConnectionOptions.Start.
|
|||
/// </summary>
|
|||
public class ServerStartOptions : ConnectionOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Listen address (e.g. 'tcp:host=localhost').
|
|||
/// </summary>
|
|||
public string Address { get; set; } |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Event data for the ServiceOwnerChanged event.
|
|||
/// </summary>
|
|||
public struct ServiceOwnerChangedEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an instance of ServiceOwnerChangedEventArgs.
|
|||
/// </summary>
|
|||
/// <param name="serviceName">The name of the service.</param>
|
|||
/// <param name="oldOwner">The previous owner of the service.</param>
|
|||
/// <param name="newOwner">The new owner of the service.</param>
|
|||
public ServiceOwnerChangedEventArgs(string serviceName, string oldOwner, string newOwner) |
|||
{ |
|||
ServiceName = serviceName; |
|||
OldOwner = oldOwner; |
|||
NewOwner = newOwner; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Name of the service.
|
|||
/// </summary>
|
|||
public string ServiceName { get; } |
|||
|
|||
/// <summary>
|
|||
/// Local name of the previous owner. <c>null</c> when there is no previous owner.
|
|||
/// </summary>
|
|||
public string OldOwner { get; internal set; } |
|||
|
|||
/// <summary>
|
|||
/// Local name of the new owner. <c>null</c> when there is no new owner.
|
|||
/// </summary>
|
|||
public string NewOwner { get; } |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Options for service name registration.
|
|||
/// </summary>
|
|||
[Flags] |
|||
public enum ServiceRegistrationOptions |
|||
{ |
|||
/// <summary>No options.</summary>
|
|||
None = 0, |
|||
/// <summary>Replace the existing owner.</summary>
|
|||
ReplaceExisting = 1, |
|||
/// <summary>Allow registration to be replaced.</summary>
|
|||
AllowReplacement = 2, |
|||
/// <summary>Default (<c>ReplaceExisting | AllowReplacement</c>)</summary>
|
|||
Default = ReplaceExisting | AllowReplacement |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Result of the service activation request.
|
|||
/// </summary>
|
|||
public enum ServiceStartResult : uint |
|||
{ |
|||
/// <summary>The service was started.</summary>
|
|||
Started = 1, |
|||
/// <summary>The service was already running.</summary>
|
|||
AlreadyRunning = 2 |
|||
} |
|||
} |
|||
@ -1,83 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System.Text; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
internal class SignalMatchRule |
|||
{ |
|||
public string Interface { get; set; } |
|||
public string Member { get; set; } |
|||
public ObjectPath2? Path { get; set; } |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
int hash = 17; |
|||
hash = hash * 23 + (Interface == null ? 0 : Interface.GetHashCode()); |
|||
hash = hash * 23 + (Member == null ? 0 : Member.GetHashCode()); |
|||
hash = hash * 23 + Path.GetHashCode(); |
|||
return hash; |
|||
} |
|||
|
|||
public override bool Equals(object o) |
|||
{ |
|||
SignalMatchRule r = o as SignalMatchRule; |
|||
if (o == null) |
|||
return false; |
|||
|
|||
return Interface == r.Interface && |
|||
Member == r.Member && |
|||
Path == r.Path; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return ToStringWithSender(null); |
|||
} |
|||
|
|||
private string ToStringWithSender(string sender) |
|||
{ |
|||
StringBuilder sb = new StringBuilder(); |
|||
|
|||
Append(sb, "type", "signal"); |
|||
|
|||
if (Interface != null) |
|||
{ |
|||
Append(sb, "interface", Interface); |
|||
} |
|||
if (Member != null) |
|||
{ |
|||
Append(sb, "member", Member); |
|||
} |
|||
if (Path != null) |
|||
{ |
|||
Append(sb, "path", Path.Value); |
|||
} |
|||
if (sender != null) |
|||
{ |
|||
Append(sb, "sender", sender); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
protected static void Append(StringBuilder sb, string key, object value) |
|||
{ |
|||
Append(sb, key, value.ToString()); |
|||
} |
|||
|
|||
static void Append(StringBuilder sb, string key, string value) |
|||
{ |
|||
if (sb.Length != 0) |
|||
sb.Append(','); |
|||
|
|||
sb.Append(key); |
|||
sb.Append("='"); |
|||
sb.Append(value.Replace(@"\", @"\\").Replace(@"'", @"\'")); |
|||
sb.Append('\''); |
|||
} |
|||
} |
|||
} |
|||
@ -1,63 +0,0 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// Helper class for implementing D-Bus signals.
|
|||
/// </summary>
|
|||
public static class SignalWatcher |
|||
{ |
|||
private class Disposable : IDisposable |
|||
{ |
|||
public Disposable(Action disposeAction) |
|||
{ |
|||
_disposeAction = disposeAction; |
|||
} |
|||
public void Dispose() |
|||
{ |
|||
_disposeAction(); |
|||
} |
|||
private Action _disposeAction; |
|||
} |
|||
|
|||
private static IDisposable Add(object o, string eventName, object handler) |
|||
{ |
|||
var eventInfo = o.GetType().GetEvent(eventName); |
|||
var addMethod = eventInfo.GetAddMethod(); |
|||
var removeMethod = eventInfo.GetRemoveMethod(); |
|||
addMethod.Invoke(o, new object[] { handler }); |
|||
Action disposeAction = () => removeMethod.Invoke(o, new object[] { handler }); |
|||
return new Disposable(disposeAction); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emits on the handler when the event is raised and returns an IDisposable that removes the handler.
|
|||
/// </summary>
|
|||
/// <param name="o">Object that emits events.</param>
|
|||
/// <param name="eventName">Name of the event.</param>
|
|||
/// <param name="handler">Action to be invoked when the event is raised.</param>
|
|||
/// <returns>
|
|||
/// Disposable that removes the handler from the event.
|
|||
/// </returns>
|
|||
public static Task<IDisposable> AddAsync<T>(object o, string eventName, Action<T> handler) |
|||
{ |
|||
return Task.FromResult(Add(o, eventName, handler)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emits on the handler when the event is raised and returns an IDisposable that removes the handler.
|
|||
/// </summary>
|
|||
/// <param name="o">Object that emits events.</param>
|
|||
/// <param name="eventName">Name of the event.</param>
|
|||
/// <param name="handler">Action to be invoked when the event is raised.</param>
|
|||
/// <returns>
|
|||
/// Disposable that removes the handler from the event.
|
|||
/// </returns>
|
|||
public static Task<IDisposable> AddAsync(object o, string eventName, Action handler) |
|||
{ |
|||
return Task.FromResult(Add(o, eventName, handler)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,899 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Tmds.DBus.CodeGen; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
/// <summary>
|
|||
/// D-Bus type signature.
|
|||
/// </summary>
|
|||
public struct Signature |
|||
{ |
|||
internal static readonly Signature Empty = new Signature (String.Empty); |
|||
internal static readonly Signature ArraySig = Allocate (DType.Array); |
|||
internal static readonly Signature ByteSig = Allocate (DType.Byte); |
|||
internal static readonly Signature DictEntryBegin = Allocate (DType.DictEntryBegin); |
|||
internal static readonly Signature DictEntryEnd = Allocate (DType.DictEntryEnd); |
|||
internal static readonly Signature Int32Sig = Allocate (DType.Int32); |
|||
internal static readonly Signature UInt16Sig = Allocate (DType.UInt16); |
|||
internal static readonly Signature UInt32Sig = Allocate (DType.UInt32); |
|||
internal static readonly Signature StringSig = Allocate (DType.String); |
|||
internal static readonly Signature StructBegin = Allocate (DType.StructBegin); |
|||
internal static readonly Signature StructEnd = Allocate (DType.StructEnd); |
|||
internal static readonly Signature ObjectPathSig = Allocate (DType.ObjectPath); |
|||
internal static readonly Signature SignatureSig = Allocate (DType.Signature); |
|||
internal static readonly Signature SignatureUnixFd = Allocate (DType.UnixFd); |
|||
internal static readonly Signature VariantSig = Allocate (DType.Variant); |
|||
internal static readonly Signature BoolSig = Allocate(DType.Boolean); |
|||
internal static readonly Signature DoubleSig = Allocate(DType.Double); |
|||
internal static readonly Signature Int16Sig = Allocate(DType.Int16); |
|||
internal static readonly Signature Int64Sig = Allocate(DType.Int64); |
|||
internal static readonly Signature SingleSig = Allocate(DType.Single); |
|||
internal static readonly Signature UInt64Sig = Allocate(DType.UInt64); |
|||
internal static readonly Signature StructBeginSig = Allocate(DType.StructBegin); |
|||
internal static readonly Signature StructEndSig = Allocate(DType.StructEnd); |
|||
internal static readonly Signature DictEntryBeginSig = Allocate(DType.DictEntryBegin); |
|||
internal static readonly Signature DictEntryEndSig = Allocate(DType.DictEntryEnd); |
|||
|
|||
private byte[] _data; |
|||
|
|||
/// <summary>
|
|||
/// Determines whether two specified Signatures have the same value.
|
|||
/// </summary>
|
|||
public static bool operator== (Signature a, Signature b) |
|||
{ |
|||
if (a._data == b._data) |
|||
return true; |
|||
|
|||
if (a._data == null) |
|||
return false; |
|||
|
|||
if (b._data == null) |
|||
return false; |
|||
|
|||
if (a._data.Length != b._data.Length) |
|||
return false; |
|||
|
|||
for (int i = 0 ; i != a._data.Length ; i++) |
|||
if (a._data[i] != b._data[i]) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether two specified Signatures have different values.
|
|||
/// </summary>
|
|||
public static bool operator!=(Signature a, Signature b) |
|||
{ |
|||
return !(a == b); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified object is equal to the current object.
|
|||
/// </summary>
|
|||
public override bool Equals (object o) |
|||
{ |
|||
if (o == null) |
|||
return false; |
|||
|
|||
if (!(o is Signature)) |
|||
return false; |
|||
|
|||
return this == (Signature)o; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this Signature.
|
|||
/// </summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
if (_data == null) |
|||
{ |
|||
return 0; |
|||
} |
|||
int hash = 17; |
|||
for(int i = 0; i < _data.Length; i++) |
|||
{ |
|||
hash = hash * 31 + _data[i].GetHashCode(); |
|||
} |
|||
return hash; |
|||
} |
|||
|
|||
internal static Signature Concat (Signature s1, Signature s2) |
|||
{ |
|||
if (s1._data == null && s2._data == null) |
|||
return Signature.Empty; |
|||
|
|||
if (s1._data == null) |
|||
return s2; |
|||
|
|||
if (s2._data == null) |
|||
return s1; |
|||
|
|||
if (s1.Length + s2.Length == 0) |
|||
return Signature.Empty; |
|||
|
|||
byte[] data = new byte[s1._data.Length + s2._data.Length]; |
|||
s1._data.CopyTo (data, 0); |
|||
s2._data.CopyTo (data, s1._data.Length); |
|||
return Signature.Take (data); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new Signature.
|
|||
/// </summary>
|
|||
/// <param name="value">signature.</param>
|
|||
public Signature(string value) |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException ("value"); |
|||
if (!IsValid (value)) |
|||
throw new ArgumentException (string.Format ("'{0}' is not a valid signature", value), "value"); |
|||
|
|||
foreach (var c in value) |
|||
if (!Enum.IsDefined (typeof (DType), (byte) c)) |
|||
throw new ArgumentException (string.Format ("{0} is not a valid dbus type", c)); |
|||
|
|||
if (value.Length == 0) { |
|||
_data = Array.Empty<byte>(); |
|||
} else if (value.Length == 1) { |
|||
_data = DataForDType ((DType)value[0]); |
|||
} else { |
|||
_data = Encoding.ASCII.GetBytes (value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new Signature.
|
|||
/// </summary>
|
|||
/// <param name="value">signature.</param>
|
|||
public static implicit operator Signature(string value) |
|||
{ |
|||
return new Signature(value); |
|||
} |
|||
|
|||
// Basic validity is to check that every "opening" DType has a corresponding closing DType
|
|||
internal static bool IsValid (string strSig) |
|||
{ |
|||
int structCount = 0; |
|||
int dictCount = 0; |
|||
|
|||
foreach (char c in strSig) { |
|||
switch ((DType)c) { |
|||
case DType.StructBegin: |
|||
structCount++; |
|||
break; |
|||
case DType.StructEnd: |
|||
structCount--; |
|||
break; |
|||
case DType.DictEntryBegin: |
|||
dictCount++; |
|||
break; |
|||
case DType.DictEntryEnd: |
|||
dictCount--; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return structCount == 0 && dictCount == 0; |
|||
} |
|||
|
|||
internal static Signature Take (byte[] value) |
|||
{ |
|||
Signature sig; |
|||
|
|||
if (value.Length == 0) { |
|||
sig._data = Empty._data; |
|||
return sig; |
|||
} |
|||
|
|||
if (value.Length == 1) { |
|||
sig._data = DataForDType ((DType)value[0]); |
|||
return sig; |
|||
} |
|||
|
|||
sig._data = value; |
|||
return sig; |
|||
} |
|||
|
|||
internal static byte[] DataForDType (DType value) |
|||
{ |
|||
switch (value) { |
|||
case DType.Byte: return ByteSig._data; |
|||
case DType.Boolean: return BoolSig._data; |
|||
case DType.Int16: return Int16Sig._data; |
|||
case DType.UInt16: return UInt16Sig._data; |
|||
case DType.Int32: return Int32Sig._data; |
|||
case DType.UInt32: return UInt32Sig._data; |
|||
case DType.Int64: return Int64Sig._data; |
|||
case DType.UInt64: return UInt64Sig._data; |
|||
case DType.Single: return SingleSig._data; |
|||
case DType.Double: return DoubleSig._data; |
|||
case DType.String: return StringSig._data; |
|||
case DType.ObjectPath: return ObjectPathSig._data; |
|||
case DType.Signature: return SignatureSig._data; |
|||
case DType.Array: return ArraySig._data; |
|||
case DType.Variant: return VariantSig._data; |
|||
case DType.StructBegin: return StructBeginSig._data; |
|||
case DType.StructEnd: return StructEndSig._data; |
|||
case DType.DictEntryBegin: return DictEntryBeginSig._data; |
|||
case DType.DictEntryEnd: return DictEntryEndSig._data; |
|||
default: |
|||
return new byte[] {(byte)value}; |
|||
} |
|||
} |
|||
|
|||
private static Signature Allocate (DType value) |
|||
{ |
|||
Signature sig; |
|||
sig._data = new byte[] {(byte)value}; |
|||
return sig; |
|||
} |
|||
|
|||
internal Signature (DType value) |
|||
{ |
|||
this._data = DataForDType (value); |
|||
} |
|||
|
|||
internal byte[] GetBuffer () |
|||
{ |
|||
return _data; |
|||
} |
|||
|
|||
internal DType this[int index] |
|||
{ |
|||
get { |
|||
return (DType)_data[index]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Length of the Signature.
|
|||
/// </summary>
|
|||
public int Length |
|||
{ |
|||
get { |
|||
return _data != null ? _data.Length : 0; |
|||
} |
|||
} |
|||
|
|||
internal string Value |
|||
{ |
|||
get { |
|||
if (_data == null) |
|||
return String.Empty; |
|||
|
|||
return Encoding.ASCII.GetString (_data); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a string that represents the current object.
|
|||
/// </summary>
|
|||
public override string ToString () |
|||
{ |
|||
return Value; |
|||
} |
|||
|
|||
internal static Signature MakeArray (Signature signature) |
|||
{ |
|||
if (!signature.IsSingleCompleteType) |
|||
throw new ArgumentException ("The type of an array must be a single complete type", "signature"); |
|||
return Concat(Signature.ArraySig, signature); |
|||
} |
|||
|
|||
internal static Signature MakeStruct (Signature signature) |
|||
{ |
|||
if (signature == Signature.Empty) |
|||
throw new ArgumentException ("Cannot create a struct with no fields", "signature"); |
|||
|
|||
return Concat(Concat(Signature.StructBegin, signature), Signature.StructEnd); |
|||
} |
|||
|
|||
internal static Signature MakeDictEntry (Signature keyType, Signature valueType) |
|||
{ |
|||
if (!keyType.IsSingleCompleteType) |
|||
throw new ArgumentException ("Signature must be a single complete type", "keyType"); |
|||
if (!valueType.IsSingleCompleteType) |
|||
throw new ArgumentException ("Signature must be a single complete type", "valueType"); |
|||
|
|||
return Concat(Concat(Concat(Signature.DictEntryBegin, keyType), valueType), Signature.DictEntryEnd); |
|||
} |
|||
|
|||
internal static Signature MakeDict (Signature keyType, Signature valueType) |
|||
{ |
|||
return MakeArray (MakeDictEntry (keyType, valueType)); |
|||
} |
|||
|
|||
internal int Alignment |
|||
{ |
|||
get { |
|||
if (_data.Length == 0) |
|||
return 0; |
|||
|
|||
return ProtocolInformation.GetAlignment (this[0]); |
|||
} |
|||
} |
|||
|
|||
internal int GetSize (DType dtype) |
|||
{ |
|||
switch (dtype) { |
|||
case DType.Byte: |
|||
return 1; |
|||
case DType.Boolean: |
|||
return 4; |
|||
case DType.Int16: |
|||
case DType.UInt16: |
|||
return 2; |
|||
case DType.Int32: |
|||
case DType.UInt32: |
|||
case DType.UnixFd: |
|||
return 4; |
|||
case DType.Int64: |
|||
case DType.UInt64: |
|||
return 8; |
|||
case DType.Single: |
|||
return 4; |
|||
case DType.Double: |
|||
return 8; |
|||
case DType.String: |
|||
case DType.ObjectPath: |
|||
case DType.Signature: |
|||
case DType.Array: |
|||
case DType.StructBegin: |
|||
case DType.Variant: |
|||
case DType.DictEntryBegin: |
|||
return -1; |
|||
case DType.Invalid: |
|||
default: |
|||
throw new ProtocolException("Cannot determine size of unknown D-Bus type: " + dtype); |
|||
} |
|||
} |
|||
|
|||
internal bool GetFixedSize (ref int size) |
|||
{ |
|||
if (size < 0) |
|||
return false; |
|||
|
|||
if (_data.Length == 0) |
|||
return true; |
|||
|
|||
// Sensible?
|
|||
size = ProtocolInformation.Padded (size, Alignment); |
|||
|
|||
if (_data.Length == 1) { |
|||
int valueSize = GetSize (this[0]); |
|||
|
|||
if (valueSize == -1) |
|||
return false; |
|||
|
|||
size += valueSize; |
|||
return true; |
|||
} |
|||
|
|||
if (IsStructlike) { |
|||
foreach (Signature sig in GetParts ()) |
|||
if (!sig.GetFixedSize (ref size)) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
if (IsArray || IsDict) |
|||
return false; |
|||
|
|||
if (IsStruct) { |
|||
foreach (Signature sig in GetFieldSignatures ()) |
|||
if (!sig.GetFixedSize (ref size)) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
// Any other cases?
|
|||
throw new Exception (); |
|||
} |
|||
|
|||
internal bool IsSingleCompleteType |
|||
{ |
|||
get { |
|||
if (_data.Length == 0) |
|||
return true; |
|||
var checker = new SignatureChecker (_data); |
|||
return checker.CheckSignature (); |
|||
} |
|||
} |
|||
|
|||
internal bool IsStruct |
|||
{ |
|||
get { |
|||
if (Length < 2) |
|||
return false; |
|||
|
|||
if (this[0] != DType.StructBegin) |
|||
return false; |
|||
|
|||
// FIXME: Incorrect! What if this is in fact a Structlike starting and finishing with structs?
|
|||
if (this[Length - 1] != DType.StructEnd) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
internal bool IsStructlike |
|||
{ |
|||
get { |
|||
if (Length < 2) |
|||
return false; |
|||
|
|||
if (IsArray) |
|||
return false; |
|||
|
|||
if (IsDict) |
|||
return false; |
|||
|
|||
if (IsStruct) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
internal bool IsDict |
|||
{ |
|||
get { |
|||
if (Length < 3) |
|||
return false; |
|||
|
|||
if (!IsArray) |
|||
return false; |
|||
|
|||
// 0 is 'a'
|
|||
if (this[1] != DType.DictEntryBegin) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
internal bool IsArray |
|||
{ |
|||
get { |
|||
if (Length < 2) |
|||
return false; |
|||
|
|||
if (this[0] != DType.Array) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
internal Type ToType () |
|||
{ |
|||
int pos = 0; |
|||
Type ret = ToType (ref pos); |
|||
if (pos != _data.Length) |
|||
throw new ProtocolException("Signature '" + Value + "' is not a single complete type"); |
|||
return ret; |
|||
} |
|||
|
|||
internal IEnumerable<Signature> GetFieldSignatures () |
|||
{ |
|||
if (this == Signature.Empty || this[0] != DType.StructBegin) |
|||
throw new ProtocolException("Not a struct"); |
|||
|
|||
for (int pos = 1 ; pos < _data.Length - 1 ;) |
|||
yield return GetNextSignature (ref pos); |
|||
} |
|||
|
|||
internal void GetDictEntrySignatures (out Signature sigKey, out Signature sigValue) |
|||
{ |
|||
if (this == Signature.Empty || this[0] != DType.DictEntryBegin) |
|||
throw new ProtocolException("Not a DictEntry"); |
|||
|
|||
int pos = 1; |
|||
sigKey = GetNextSignature (ref pos); |
|||
sigValue = GetNextSignature (ref pos); |
|||
} |
|||
|
|||
internal IEnumerable<Signature> GetParts () |
|||
{ |
|||
if (_data == null) |
|||
yield break; |
|||
for (int pos = 0 ; pos < _data.Length ;) { |
|||
yield return GetNextSignature (ref pos); |
|||
} |
|||
} |
|||
|
|||
internal Signature GetNextSignature (ref int pos) |
|||
{ |
|||
if (_data == null) |
|||
return Signature.Empty; |
|||
|
|||
DType dtype = (DType)_data[pos++]; |
|||
|
|||
switch (dtype) { |
|||
//case DType.Invalid:
|
|||
// return typeof (void);
|
|||
case DType.Array: |
|||
//peek to see if this is in fact a dictionary
|
|||
if ((DType)_data[pos] == DType.DictEntryBegin) { |
|||
//skip over the {
|
|||
pos++; |
|||
Signature keyType = GetNextSignature (ref pos); |
|||
Signature valueType = GetNextSignature (ref pos); |
|||
//skip over the }
|
|||
pos++; |
|||
return Signature.MakeDict (keyType, valueType); |
|||
} else { |
|||
Signature elementType = GetNextSignature (ref pos); |
|||
return MakeArray (elementType); |
|||
} |
|||
//case DType.DictEntryBegin: // FIXME: DictEntries should be handled separately.
|
|||
case DType.StructBegin: |
|||
//List<Signature> fieldTypes = new List<Signature> ();
|
|||
Signature fieldsSig = Signature.Empty; |
|||
while ((DType)_data[pos] != DType.StructEnd) |
|||
{ |
|||
fieldsSig = Concat(fieldsSig, GetNextSignature (ref pos)); |
|||
} |
|||
//skip over the )
|
|||
pos++; |
|||
return Signature.MakeStruct (fieldsSig); |
|||
//return fieldsSig;
|
|||
case DType.DictEntryBegin: |
|||
Signature sigKey = GetNextSignature (ref pos); |
|||
Signature sigValue = GetNextSignature (ref pos); |
|||
//skip over the }
|
|||
pos++; |
|||
return Signature.MakeDictEntry (sigKey, sigValue); |
|||
default: |
|||
return new Signature (dtype); |
|||
} |
|||
} |
|||
|
|||
internal Type ToType (ref int pos) |
|||
{ |
|||
if (_data == null) |
|||
return typeof (void); |
|||
|
|||
DType dtype = (DType)_data[pos++]; |
|||
|
|||
switch (dtype) { |
|||
case DType.Invalid: |
|||
return typeof (void); |
|||
case DType.Byte: |
|||
return typeof (byte); |
|||
case DType.Boolean: |
|||
return typeof (bool); |
|||
case DType.Int16: |
|||
return typeof (short); |
|||
case DType.UInt16: |
|||
return typeof (ushort); |
|||
case DType.Int32: |
|||
return typeof (int); |
|||
case DType.UInt32: |
|||
return typeof (uint); |
|||
case DType.Int64: |
|||
return typeof (long); |
|||
case DType.UInt64: |
|||
return typeof (ulong); |
|||
case DType.Single: ////not supported by libdbus at time of writing
|
|||
return typeof (float); |
|||
case DType.Double: |
|||
return typeof (double); |
|||
case DType.String: |
|||
return typeof (string); |
|||
case DType.ObjectPath: |
|||
return typeof (ObjectPath2); |
|||
case DType.Signature: |
|||
return typeof (Signature); |
|||
case DType.UnixFd: |
|||
return typeof (CloseSafeHandle); |
|||
case DType.Array: |
|||
//peek to see if this is in fact a dictionary
|
|||
if ((DType)_data[pos] == DType.DictEntryBegin) { |
|||
//skip over the {
|
|||
pos++; |
|||
Type keyType = ToType (ref pos); |
|||
Type valueType = ToType (ref pos); |
|||
//skip over the }
|
|||
pos++; |
|||
return typeof(IDictionary<,>).MakeGenericType (new [] { keyType, valueType}); |
|||
} else { |
|||
return ToType (ref pos).MakeArrayType (); |
|||
} |
|||
case DType.StructBegin: |
|||
List<Type> innerTypes = new List<Type> (); |
|||
while (((DType)_data[pos]) != DType.StructEnd) |
|||
innerTypes.Add (ToType (ref pos)); |
|||
// go over the struct end
|
|||
pos++; |
|||
return TypeOfValueTupleOf(innerTypes.ToArray ()); |
|||
case DType.DictEntryBegin: |
|||
return typeof (System.Collections.Generic.KeyValuePair<,>); |
|||
case DType.Variant: |
|||
return typeof (object); |
|||
default: |
|||
throw new NotSupportedException ("Parsing or converting this signature is not yet supported (signature was '" + Value + "'), at DType." + dtype); |
|||
} |
|||
} |
|||
|
|||
internal static Signature GetSig (object[] objs) |
|||
{ |
|||
return GetSig (objs.Select(o => o.GetType()).ToArray(), isCompileTimeType: true); |
|||
} |
|||
|
|||
internal static Signature GetSig (Type[] types, bool isCompileTimeType) |
|||
{ |
|||
if (types == null) |
|||
throw new ArgumentNullException ("types"); |
|||
|
|||
Signature sig = Signature.Empty; |
|||
|
|||
foreach (Type type in types) |
|||
{ |
|||
sig = Concat(sig, GetSig (type, isCompileTimeType)); |
|||
} |
|||
|
|||
return sig; |
|||
} |
|||
|
|||
internal static Signature GetSig (Type type, bool isCompileTimeType) |
|||
{ |
|||
if (type == null) |
|||
throw new ArgumentNullException ("type"); |
|||
|
|||
if (type.GetTypeInfo().IsEnum) |
|||
{ |
|||
type = Enum.GetUnderlyingType(type); |
|||
} |
|||
|
|||
if (type == typeof(bool)) |
|||
{ |
|||
return BoolSig; |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return ByteSig; |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return DoubleSig; |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return Int16Sig; |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return Int32Sig; |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return Int64Sig; |
|||
} |
|||
else if (type == typeof(ObjectPath2)) |
|||
{ |
|||
return ObjectPathSig; |
|||
} |
|||
else if (type == typeof(Signature)) |
|||
{ |
|||
return SignatureSig; |
|||
} |
|||
else if (type == typeof(string)) |
|||
{ |
|||
return StringSig; |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return SingleSig; |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return UInt16Sig; |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return UInt32Sig; |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return UInt64Sig; |
|||
} |
|||
else if (type == typeof(object)) |
|||
{ |
|||
return VariantSig; |
|||
} |
|||
else if (type == typeof(IDBusObject)) |
|||
{ |
|||
return ObjectPathSig; |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType)) |
|||
{ |
|||
return ObjectPathSig; |
|||
} |
|||
|
|||
Type elementType; |
|||
var enumerableType = ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType); |
|||
if (enumerableType != ArgTypeInspector.EnumerableType.NotEnumerable) |
|||
{ |
|||
if ((enumerableType == ArgTypeInspector.EnumerableType.EnumerableKeyValuePair) || |
|||
(enumerableType == ArgTypeInspector.EnumerableType.GenericDictionary) || |
|||
(enumerableType == ArgTypeInspector.EnumerableType.AttributeDictionary)) |
|||
{ |
|||
Type keyType = elementType.GenericTypeArguments[0]; |
|||
Type valueType = elementType.GenericTypeArguments[1]; |
|||
return Signature.MakeDict(GetSig(keyType, isCompileTimeType: true), GetSig(valueType, isCompileTimeType: true)); |
|||
} |
|||
else // Enumerable
|
|||
{ |
|||
return MakeArray(GetSig(elementType, isCompileTimeType: true)); |
|||
} |
|||
} |
|||
|
|||
bool isValueTuple; |
|||
if (ArgTypeInspector.IsStructType(type, out isValueTuple)) |
|||
{ |
|||
Signature sig = Signature.Empty; |
|||
var fields = ArgTypeInspector.GetStructFields(type, isValueTuple); |
|||
for (int i = 0; i < fields.Length;) |
|||
{ |
|||
var fi = fields[i]; |
|||
if (i == 7 && isValueTuple) |
|||
{ |
|||
fields = ArgTypeInspector.GetStructFields(fi.FieldType, isValueTuple); |
|||
i = 0; |
|||
} |
|||
else |
|||
{ |
|||
sig = Concat(sig, GetSig(fi.FieldType, isCompileTimeType: true)); |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
return Signature.MakeStruct(sig); |
|||
} |
|||
|
|||
if (ArgTypeInspector.IsSafeHandleType(type)) |
|||
{ |
|||
return Signature.SignatureUnixFd; |
|||
} |
|||
|
|||
throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'"); |
|||
} |
|||
|
|||
private static Type TypeOfValueTupleOf(Type[] innerTypes) |
|||
{ |
|||
if (innerTypes == null || innerTypes.Length == 0) |
|||
throw new NotSupportedException($"ValueTuple of length {innerTypes?.Length} is not supported"); |
|||
if (innerTypes.Length > 7) |
|||
{ |
|||
innerTypes = new [] { innerTypes[0], innerTypes[1], innerTypes[2], innerTypes[3], innerTypes[4], innerTypes[5], innerTypes[6], TypeOfValueTupleOf(innerTypes.Skip(7).ToArray()) }; |
|||
} |
|||
|
|||
Type structType = null; |
|||
switch (innerTypes.Length) { |
|||
case 1: |
|||
structType = typeof(ValueTuple<>); |
|||
break; |
|||
case 2: |
|||
structType = typeof(ValueTuple<,>); |
|||
break; |
|||
case 3: |
|||
structType = typeof(ValueTuple<,,>); |
|||
break; |
|||
case 4: |
|||
structType = typeof(ValueTuple<,,,>); |
|||
break; |
|||
case 5: |
|||
structType = typeof(ValueTuple<,,,,>); |
|||
break; |
|||
case 6: |
|||
structType = typeof(ValueTuple<,,,,,>); |
|||
break; |
|||
case 7: |
|||
structType = typeof(ValueTuple<,,,,,,>); |
|||
break; |
|||
case 8: |
|||
structType = typeof(ValueTuple<,,,,,,,>); |
|||
break; |
|||
} |
|||
return structType.MakeGenericType(innerTypes); |
|||
} |
|||
|
|||
class SignatureChecker |
|||
{ |
|||
byte[] data; |
|||
int pos; |
|||
|
|||
internal SignatureChecker (byte[] data) |
|||
{ |
|||
this.data = data; |
|||
} |
|||
|
|||
internal bool CheckSignature () |
|||
{ |
|||
return SingleType () ? pos == data.Length : false; |
|||
} |
|||
|
|||
bool SingleType () |
|||
{ |
|||
if (pos >= data.Length) |
|||
return false; |
|||
|
|||
//Console.WriteLine ((DType)data[pos]);
|
|||
|
|||
switch ((DType)data[pos]) { |
|||
// Simple Type
|
|||
case DType.Byte: |
|||
case DType.Boolean: |
|||
case DType.Int16: |
|||
case DType.UInt16: |
|||
case DType.Int32: |
|||
case DType.UInt32: |
|||
case DType.Int64: |
|||
case DType.UInt64: |
|||
case DType.Single: |
|||
case DType.Double: |
|||
case DType.String: |
|||
case DType.ObjectPath: |
|||
case DType.Signature: |
|||
case DType.Variant: |
|||
pos += 1; |
|||
return true; |
|||
case DType.Array: |
|||
pos += 1; |
|||
return ArrayType (); |
|||
case DType.StructBegin: |
|||
pos += 1; |
|||
return StructType (); |
|||
case DType.DictEntryBegin: |
|||
pos += 1; |
|||
return DictType (); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
bool ArrayType () |
|||
{ |
|||
return SingleType (); |
|||
} |
|||
|
|||
bool DictType () |
|||
{ |
|||
bool result = SingleType () && SingleType () && ((DType)data[pos]) == DType.DictEntryEnd; |
|||
if (result) |
|||
pos += 1; |
|||
return result; |
|||
} |
|||
|
|||
bool StructType () |
|||
{ |
|||
if (pos >= data.Length) |
|||
return false; |
|||
while (((DType)data[pos]) != DType.StructEnd) { |
|||
if (!SingleType ()) |
|||
return false; |
|||
if (pos >= data.Length) |
|||
return false; |
|||
} |
|||
pos += 1; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks> |
|||
<Nullable>enable</Nullable> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -1,191 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.Transports |
|||
{ |
|||
class LocalServer : IMessageStream |
|||
{ |
|||
private readonly object _gate = new object(); |
|||
private readonly DBusConnection _connection; |
|||
private IMessageStream[] _clients; |
|||
private Socket _serverSocket; |
|||
private bool _started; |
|||
|
|||
public LocalServer(DBusConnection connection) |
|||
{ |
|||
_connection = connection; |
|||
_clients = Array.Empty<IMessageStream>(); |
|||
} |
|||
|
|||
public async Task<string> StartAsync(string address) |
|||
{ |
|||
if (address == null) |
|||
throw new ArgumentNullException(nameof(address)); |
|||
|
|||
var entries = AddressEntry.ParseEntries(address); |
|||
if (entries.Length != 1) |
|||
{ |
|||
throw new ArgumentException("Address must contain a single entry.", nameof(address)); |
|||
} |
|||
var entry = entries[0]; |
|||
var endpoints = await entry.ResolveAsync(listen: true).ConfigureAwait(false); |
|||
if (endpoints.Length == 0) |
|||
{ |
|||
throw new ArgumentException("Address does not resolve to an endpoint.", nameof(address)); |
|||
} |
|||
|
|||
lock (_gate) |
|||
{ |
|||
if (IsDisposed) |
|||
{ |
|||
throw new ObjectDisposedException(typeof(LocalServer).FullName); |
|||
} |
|||
if (_started) |
|||
{ |
|||
throw new InvalidOperationException("Server is already started."); |
|||
} |
|||
_started = true; |
|||
|
|||
var endpoint = endpoints[0]; |
|||
if (endpoint is IPEndPoint ipEndPoint) |
|||
{ |
|||
_serverSocket = new Socket(ipEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
|||
} |
|||
else if (endpoint is UnixDomainSocketEndPoint unixEndPoint) |
|||
{ |
|||
_serverSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); |
|||
if (unixEndPoint.Path[0] == '\0') |
|||
{ |
|||
address = $"unix:abstract={unixEndPoint.Path.Substring(1)}"; |
|||
} |
|||
else |
|||
{ |
|||
address = $"unix:path={unixEndPoint.Path}"; |
|||
} |
|||
} |
|||
_serverSocket.Bind(endpoint); |
|||
_serverSocket.Listen(10); |
|||
AcceptConnections(); |
|||
|
|||
if (endpoint is IPEndPoint) |
|||
{ |
|||
var boundEndPoint = _serverSocket.LocalEndPoint as IPEndPoint; |
|||
address = $"tcp:host={boundEndPoint.Address},port={boundEndPoint.Port}"; |
|||
} |
|||
return address; |
|||
} |
|||
} |
|||
|
|||
public async void AcceptConnections() |
|||
{ |
|||
while (true) |
|||
{ |
|||
Socket clientSocket = null; |
|||
try |
|||
{ |
|||
try |
|||
{ |
|||
clientSocket = await _serverSocket.AcceptAsync().ConfigureAwait(false); |
|||
} |
|||
catch (ObjectDisposedException) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
var client = await Transport.AcceptAsync(clientSocket, |
|||
supportsFdPassing: _serverSocket.AddressFamily == AddressFamily.Unix).ConfigureAwait(false); |
|||
lock (_gate) |
|||
{ |
|||
if (IsDisposed) |
|||
{ |
|||
client.Dispose(); |
|||
break; |
|||
} |
|||
var clientsUpdated = new IMessageStream[_clients.Length + 1]; |
|||
Array.Copy(_clients, clientsUpdated, _clients.Length); |
|||
clientsUpdated[clientsUpdated.Length - 1] = client; |
|||
_clients = clientsUpdated; |
|||
} |
|||
_connection.ReceiveMessages(client, RemoveStream); |
|||
} |
|||
catch |
|||
{ |
|||
clientSocket?.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void RemoveStream(IMessageStream client, Exception e) |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (IsDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var clientsUpdated = new IMessageStream[_clients.Length - 1]; |
|||
for (int i = 0, j = 0; i < _clients.Length; i++) |
|||
{ |
|||
if (_clients[i] != client) |
|||
{ |
|||
clientsUpdated[j++] = _clients[i]; |
|||
} |
|||
} |
|||
_clients = clientsUpdated; |
|||
} |
|||
client.Dispose(); |
|||
} |
|||
|
|||
public void TrySendMessage(Message message) |
|||
{ |
|||
var clients = Volatile.Read(ref _clients); |
|||
foreach (var client in clients) |
|||
{ |
|||
client.TrySendMessage(message); |
|||
} |
|||
} |
|||
|
|||
public Task<Message> ReceiveMessageAsync() |
|||
{ |
|||
return Task.FromException<Message>(new NotSupportedException("Cannot determine destination peer.")); |
|||
} |
|||
|
|||
public Task SendMessageAsync(Message message) |
|||
{ |
|||
return Task.FromException(new NotSupportedException("Cannot determine destination peer.")); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
IMessageStream[] clients; |
|||
lock (_gate) |
|||
{ |
|||
if (IsDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_serverSocket?.Dispose(); |
|||
|
|||
clients = _clients; |
|||
_clients = null; |
|||
} |
|||
|
|||
foreach (var client in clients) |
|||
{ |
|||
client.Dispose(); |
|||
} |
|||
} |
|||
|
|||
private bool IsDisposed => _clients == null; |
|||
} |
|||
} |
|||
@ -1,433 +0,0 @@ |
|||
// Copyright 2006 Alp Toker <alp@atoker.com>
|
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Concurrent; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Net.Sockets; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.Transports |
|||
{ |
|||
internal class Transport : IMessageStream |
|||
{ |
|||
private struct PendingSend |
|||
{ |
|||
public Message Message; |
|||
public TaskCompletionSource<bool> CompletionSource; |
|||
} |
|||
|
|||
private static readonly byte[] _oneByteArray = new[] { (byte)0 }; |
|||
private readonly byte[] _headerReadBuffer = new byte[16]; |
|||
private readonly List<UnixFd> _fileDescriptors = new List<UnixFd>(); |
|||
private TransportSocket _socket; |
|||
private ConcurrentQueue<PendingSend> _sendQueue; |
|||
private SemaphoreSlim _sendSemaphore; |
|||
|
|||
public static async Task<IMessageStream> ConnectAsync(AddressEntry entry, ClientSetupResult connectionContext, CancellationToken cancellationToken) |
|||
{ |
|||
TransportSocket socket = await TransportSocket.ConnectAsync(entry, cancellationToken, connectionContext.SupportsFdPassing).ConfigureAwait(false); |
|||
try |
|||
{ |
|||
Transport transport = new Transport(socket); |
|||
await transport.DoClientAuth(entry.Guid, connectionContext.UserId).ConfigureAwait(false); |
|||
return transport; |
|||
} |
|||
catch |
|||
{ |
|||
socket.Dispose(); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
public static async Task<IMessageStream> AcceptAsync(Socket acceptedSocket, bool supportsFdPassing) |
|||
{ |
|||
var socket = new TransportSocket(acceptedSocket, supportsFdPassing); |
|||
Transport transport = new Transport(socket); |
|||
socket.SupportsFdPassing = await transport.DoServerAuth(socket.SupportsFdPassing).ConfigureAwait(false); |
|||
return transport; |
|||
} |
|||
|
|||
private Transport(TransportSocket socket) |
|||
{ |
|||
_socket = socket; |
|||
_sendQueue = new ConcurrentQueue<PendingSend>(); |
|||
_sendSemaphore = new SemaphoreSlim(1); |
|||
} |
|||
|
|||
public async Task<Message> ReceiveMessageAsync() |
|||
{ |
|||
try |
|||
{ |
|||
int bytesRead = await ReadCountAsync(_headerReadBuffer, 0, 16, _fileDescriptors).ConfigureAwait(false); |
|||
if (bytesRead == 0) |
|||
return null; |
|||
if (bytesRead != 16) |
|||
throw new ProtocolException("Header read length mismatch: " + bytesRead + " of expected " + "16"); |
|||
|
|||
EndianFlag endianness = (EndianFlag)_headerReadBuffer[0]; |
|||
MessageReader reader = new MessageReader(endianness, new ArraySegment<byte>(_headerReadBuffer)); |
|||
|
|||
//discard endian byte, message type and flags, which we don't care about here
|
|||
reader.Seek(3); |
|||
|
|||
byte version = reader.ReadByte(); |
|||
if (version != ProtocolInformation.Version) |
|||
throw new NotSupportedException("Protocol version '" + version.ToString() + "' is not supported"); |
|||
|
|||
uint bodyLength = reader.ReadUInt32(); |
|||
|
|||
//discard _methodSerial
|
|||
reader.ReadUInt32(); |
|||
|
|||
uint headerLength = reader.ReadUInt32(); |
|||
|
|||
int bodyLen = (int)bodyLength; |
|||
int toRead = (int)headerLength; |
|||
|
|||
//we fixup to include the padding following the header
|
|||
toRead = ProtocolInformation.Padded(toRead, 8); |
|||
|
|||
long msgLength = toRead + bodyLen; |
|||
if (msgLength > ProtocolInformation.MaxMessageLength) |
|||
throw new ProtocolException("Message length " + msgLength + " exceeds maximum allowed " + ProtocolInformation.MaxMessageLength + " bytes"); |
|||
|
|||
byte[] header = new byte[16 + toRead]; |
|||
Array.Copy(_headerReadBuffer, header, 16); |
|||
bytesRead = await ReadCountAsync(header, 16, toRead, _fileDescriptors).ConfigureAwait(false); |
|||
if (bytesRead != toRead) |
|||
throw new ProtocolException("Message header length mismatch: " + bytesRead + " of expected " + toRead); |
|||
|
|||
var messageHeader = Header.FromBytes(new ArraySegment<byte>(header)); |
|||
|
|||
byte[] body = null; |
|||
//read the body
|
|||
if (bodyLen != 0) |
|||
{ |
|||
body = new byte[bodyLen]; |
|||
|
|||
bytesRead = await ReadCountAsync(body, 0, bodyLen, _fileDescriptors).ConfigureAwait(false); |
|||
|
|||
if (bytesRead != bodyLen) |
|||
throw new ProtocolException("Message body length mismatch: " + bytesRead + " of expected " + bodyLen); |
|||
} |
|||
|
|||
if (_fileDescriptors.Count < messageHeader.NumberOfFds) |
|||
{ |
|||
throw new ProtocolException("File descriptor length mismatch: " + _fileDescriptors.Count + " of expected " + messageHeader.NumberOfFds); |
|||
} |
|||
|
|||
Message msg = new Message( |
|||
messageHeader, |
|||
body, |
|||
messageHeader.NumberOfFds == 0 ? null : |
|||
_fileDescriptors.Count == messageHeader.NumberOfFds ? _fileDescriptors.ToArray() : |
|||
_fileDescriptors.Take((int)messageHeader.NumberOfFds).ToArray() |
|||
); |
|||
|
|||
_fileDescriptors.RemoveRange(0, (int)messageHeader.NumberOfFds); |
|||
|
|||
return msg; |
|||
} |
|||
catch |
|||
{ |
|||
foreach(var fd in _fileDescriptors) |
|||
{ |
|||
CloseSafeHandle.close(fd.Handle); |
|||
} |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
private async Task<int> ReadCountAsync(byte[] buffer, int offset, int count, List<UnixFd> fileDescriptors) |
|||
{ |
|||
int read = 0; |
|||
while (read < count) |
|||
{ |
|||
int nread = await _socket.ReadAsync(buffer, offset + read, count - read, fileDescriptors).ConfigureAwait(false); |
|||
if (nread == 0) |
|||
break; |
|||
read += nread; |
|||
} |
|||
return read; |
|||
} |
|||
|
|||
private struct AuthenticationResult |
|||
{ |
|||
public bool IsAuthenticated; |
|||
public bool SupportsFdPassing; |
|||
public Guid Guid; |
|||
} |
|||
|
|||
class SplitLine |
|||
{ |
|||
public readonly string Value; |
|||
private readonly List<string> _args = new List<string>(); |
|||
|
|||
public SplitLine(string value) |
|||
{ |
|||
this.Value = value.Trim(); |
|||
_args.AddRange(Value.Split(' ')); |
|||
} |
|||
|
|||
public string this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index >= _args.Count) |
|||
return String.Empty; |
|||
return _args[index]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task<bool> DoServerAuth(bool supportsFdPassing) |
|||
{ |
|||
bool peerSupportsFdPassing = false; |
|||
// receive 1 byte
|
|||
byte[] buffer = new byte[1]; |
|||
int length = await ReadCountAsync(buffer, 0, buffer.Length, _fileDescriptors).ConfigureAwait(false); |
|||
if (length == 0) |
|||
{ |
|||
throw new IOException("Connection closed by peer"); |
|||
} |
|||
// auth
|
|||
while (true) |
|||
{ |
|||
var line = await ReadLineAsync().ConfigureAwait(false); |
|||
if (line[0] == "AUTH") |
|||
{ |
|||
if (line[1] == "ANONYMOUS") |
|||
{ |
|||
await WriteLineAsync("OK").ConfigureAwait(false); |
|||
} |
|||
else |
|||
{ |
|||
await WriteLineAsync("REJECTED ANONYMOUS").ConfigureAwait(false); |
|||
} |
|||
} |
|||
else if (line[0] == "CANCEL") |
|||
{ |
|||
await WriteLineAsync("REJECTED ANONYMOUS").ConfigureAwait(false); |
|||
} |
|||
else if (line[0] == "BEGIN") |
|||
{ |
|||
break; |
|||
} |
|||
else if (line[0] == "DATA") |
|||
{ |
|||
throw new ProtocolException("Unexpected DATA message during authentication."); |
|||
} |
|||
else if (line[0] == "ERROR") |
|||
{ } |
|||
else if (line[0] == "NEGOTIATE_UNIX_FD" && supportsFdPassing) |
|||
{ |
|||
await WriteLineAsync("AGREE_UNIX_FD").ConfigureAwait(false); |
|||
peerSupportsFdPassing = true; |
|||
} |
|||
else |
|||
{ |
|||
await WriteLineAsync("ERROR").ConfigureAwait(false); |
|||
} |
|||
} |
|||
return peerSupportsFdPassing; |
|||
} |
|||
|
|||
private async Task DoClientAuth(Guid guid, string userId) |
|||
{ |
|||
// send 1 byte
|
|||
await _socket.SendAsync(_oneByteArray, 0, 1).ConfigureAwait(false); |
|||
// auth
|
|||
var authenticationResult = await SendAuthCommands(userId).ConfigureAwait(false); |
|||
_socket.SupportsFdPassing = authenticationResult.SupportsFdPassing; |
|||
if (guid != Guid.Empty) |
|||
{ |
|||
if (guid != authenticationResult.Guid) |
|||
{ |
|||
throw new ConnectException("Authentication failure: Unexpected GUID"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task<AuthenticationResult> SendAuthCommands(string userId) |
|||
{ |
|||
string initialData = null; |
|||
if (userId != null) |
|||
{ |
|||
byte[] bs = Encoding.ASCII.GetBytes(userId); |
|||
initialData = ToHex(bs); |
|||
} |
|||
AuthenticationResult result; |
|||
var commands = new[] |
|||
{ |
|||
initialData != null ? "AUTH EXTERNAL " + initialData : null, |
|||
"AUTH ANONYMOUS" |
|||
}; |
|||
|
|||
foreach (var command in commands) |
|||
{ |
|||
if (command == null) |
|||
{ |
|||
continue; |
|||
} |
|||
result = await SendAuthCommand(command).ConfigureAwait(false); |
|||
if (result.IsAuthenticated) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
throw new ConnectException("Authentication failure"); |
|||
} |
|||
|
|||
private async Task<AuthenticationResult> SendAuthCommand(string command) |
|||
{ |
|||
AuthenticationResult result = default(AuthenticationResult); |
|||
await WriteLineAsync(command).ConfigureAwait(false); |
|||
SplitLine reply = await ReadLineAsync().ConfigureAwait(false); |
|||
|
|||
if (reply[0] == "OK") |
|||
{ |
|||
result.IsAuthenticated = true; |
|||
result.Guid = reply[1] != string.Empty ? Guid.ParseExact(reply[1], "N") : Guid.Empty; |
|||
|
|||
if (_socket.SupportsFdPassing) |
|||
{ |
|||
await WriteLineAsync("NEGOTIATE_UNIX_FD").ConfigureAwait(false); |
|||
reply = await ReadLineAsync().ConfigureAwait(false); |
|||
result.SupportsFdPassing = reply[0] == "AGREE_UNIX_FD"; |
|||
} |
|||
|
|||
await WriteLineAsync("BEGIN").ConfigureAwait(false); |
|||
return result; |
|||
} |
|||
else if (reply[0] == "REJECTED") |
|||
{ |
|||
return result; |
|||
} |
|||
else |
|||
{ |
|||
await WriteLineAsync("ERROR").ConfigureAwait(false); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
private Task WriteLineAsync(string message) |
|||
{ |
|||
message += "\r\n"; |
|||
var bytes = Encoding.ASCII.GetBytes(message); |
|||
return _socket.SendAsync(bytes, 0, bytes.Length); |
|||
} |
|||
|
|||
private async Task<SplitLine> ReadLineAsync() |
|||
{ |
|||
byte[] buffer = new byte[1]; |
|||
StringBuilder sb = new StringBuilder(); |
|||
while (true) |
|||
{ |
|||
int length = await ReadCountAsync(buffer, 0, buffer.Length, _fileDescriptors).ConfigureAwait(false); |
|||
if (length == 0) |
|||
{ |
|||
throw new IOException("Connection closed by peer"); |
|||
} |
|||
byte b = buffer[0]; |
|||
if (b == '\r') |
|||
{ |
|||
length = await ReadCountAsync(buffer, 0, buffer.Length, _fileDescriptors).ConfigureAwait(false); |
|||
b = buffer[0]; |
|||
if (b == '\n') |
|||
{ |
|||
string ln = sb.ToString(); |
|||
if (ln != string.Empty) |
|||
{ |
|||
return new SplitLine(ln); |
|||
} |
|||
else |
|||
{ |
|||
throw new ProtocolException("Received empty authentication message from server"); |
|||
} |
|||
} |
|||
throw new ProtocolException("Authentication messages from server must end with '\\r\\n'"); |
|||
} |
|||
else |
|||
{ |
|||
sb.Append((char) b); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static string ToHex(byte[] input) |
|||
{ |
|||
StringBuilder result = new StringBuilder(input.Length * 2); |
|||
string alfabeth = "0123456789abcdef"; |
|||
|
|||
foreach (byte b in input) |
|||
{ |
|||
result.Append(alfabeth[(int)(b >> 4)]); |
|||
result.Append(alfabeth[(int)(b & 0xF)]); |
|||
} |
|||
|
|||
return result.ToString(); |
|||
} |
|||
|
|||
public Task SendMessageAsync(Message message) |
|||
{ |
|||
var tcs = new TaskCompletionSource<bool>(); |
|||
var pendingSend = new PendingSend() |
|||
{ |
|||
Message = message, |
|||
CompletionSource = tcs |
|||
}; |
|||
_sendQueue.Enqueue(pendingSend); |
|||
SendPendingMessages(); |
|||
return tcs.Task; |
|||
} |
|||
|
|||
public void TrySendMessage(Message message) |
|||
{ |
|||
var pendingSend = new PendingSend() |
|||
{ |
|||
Message = message |
|||
}; |
|||
_sendQueue.Enqueue(pendingSend); |
|||
SendPendingMessages(); |
|||
} |
|||
|
|||
private async void SendPendingMessages() |
|||
{ |
|||
try |
|||
{ |
|||
await _sendSemaphore.WaitAsync().ConfigureAwait(false); |
|||
PendingSend pendingSend; |
|||
while (_sendQueue.TryDequeue(out pendingSend)) |
|||
{ |
|||
try |
|||
{ |
|||
await _socket.SendAsync(pendingSend.Message).ConfigureAwait(false); |
|||
pendingSend.CompletionSource?.SetResult(true); |
|||
} |
|||
catch (System.Exception) |
|||
{ |
|||
pendingSend.CompletionSource?.SetResult(false); |
|||
} |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
_sendSemaphore.Release(); |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_socket?.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,525 +0,0 @@ |
|||
// Copyright 2017 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus.Protocol; |
|||
|
|||
namespace Tmds.DBus.Transports |
|||
{ |
|||
using SizeT = System.UIntPtr; |
|||
internal class TransportSocket |
|||
{ |
|||
// Issue https://github.com/dotnet/corefx/issues/6807
|
|||
private static readonly PropertyInfo s_handleProperty = typeof(Socket).GetTypeInfo().GetDeclaredProperty("Handle"); |
|||
private static readonly PropertyInfo s_safehandleProperty = typeof(Socket).GetTypeInfo().GetDeclaredProperty("SafeHandle"); |
|||
|
|||
const int SOL_SOCKET = 1; |
|||
const int EINTR = 4; |
|||
const int EBADF = 9; |
|||
static readonly int EAGAIN = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 35 : 11; |
|||
const int SCM_RIGHTS = 1; |
|||
|
|||
private unsafe struct msghdr |
|||
{ |
|||
public IntPtr msg_name; //optional address
|
|||
public uint msg_namelen; //size of address
|
|||
public IOVector* msg_iov; //scatter/gather array
|
|||
public SizeT msg_iovlen; //# elements in msg_iov
|
|||
public void* msg_control; //ancillary data, see below
|
|||
public SizeT msg_controllen; //ancillary data buffer len
|
|||
public int msg_flags; //flags on received message
|
|||
} |
|||
|
|||
private unsafe struct IOVector |
|||
{ |
|||
public void* Base; |
|||
public SizeT Length; |
|||
} |
|||
|
|||
private struct cmsghdr |
|||
{ |
|||
public SizeT cmsg_len; //data byte count, including header
|
|||
public int cmsg_level; //originating protocol
|
|||
public int cmsg_type; //protocol-specific type
|
|||
} |
|||
|
|||
private unsafe struct cmsg_fd |
|||
{ |
|||
public cmsghdr hdr; |
|||
public fixed int fds[64]; |
|||
} |
|||
|
|||
private class ReadContext |
|||
{ |
|||
public TaskCompletionSource<int> Tcs; |
|||
public byte[] Buffer; |
|||
public int Offset; |
|||
public int Count; |
|||
public List<UnixFd> FileDescriptors; |
|||
} |
|||
|
|||
private class SendContext |
|||
{ |
|||
public TaskCompletionSource<object> Tcs; |
|||
} |
|||
|
|||
private int _socketFd; |
|||
private readonly object _gate = new object(); |
|||
private readonly SocketAsyncEventArgs _waitForData; |
|||
private readonly SocketAsyncEventArgs _receiveData; |
|||
private readonly SocketAsyncEventArgs _sendArgs; |
|||
private readonly List<ArraySegment<byte>> _bufferList = new List<ArraySegment<byte>>(); |
|||
private readonly Socket _socket; |
|||
private bool _supportsFdPassing; |
|||
|
|||
public TransportSocket(Socket socket, bool supportsFdPassing) |
|||
{ |
|||
_socket = socket; |
|||
_socketFd = GetFd(socket); |
|||
_supportsFdPassing = supportsFdPassing && _socketFd != -1; |
|||
|
|||
_waitForData = new SocketAsyncEventArgs(); |
|||
_waitForData.SetBuffer(Array.Empty<byte>(), 0, 0); |
|||
_waitForData.Completed += DataAvailable; |
|||
_waitForData.UserToken = new ReadContext(); |
|||
|
|||
_receiveData = new SocketAsyncEventArgs(); |
|||
_receiveData.Completed += ReadCompleted; |
|||
_receiveData.UserToken = new ReadContext(); |
|||
|
|||
_sendArgs = new SocketAsyncEventArgs(); |
|||
_sendArgs.BufferList = new List<ArraySegment<byte>>(); |
|||
_sendArgs.UserToken = new SendContext(); |
|||
_sendArgs.Completed += SendCompleted; |
|||
} |
|||
|
|||
public bool SupportsFdPassing { get => _supportsFdPassing; set { _supportsFdPassing = value; } } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
_socketFd = -1; |
|||
_socket.Dispose(); |
|||
} |
|||
} |
|||
|
|||
private void ReadCompleted(object sender, SocketAsyncEventArgs e) |
|||
{ |
|||
var readContext = _receiveData.UserToken as ReadContext; |
|||
var tcs = readContext.Tcs; |
|||
readContext.Tcs = null; |
|||
if (e.SocketError == SocketError.Success) |
|||
{ |
|||
tcs.SetResult(e.BytesTransferred); |
|||
} |
|||
else |
|||
{ |
|||
tcs.SetException(new SocketException((int)e.SocketError)); |
|||
} |
|||
} |
|||
|
|||
private void DataAvailable(object sender, SocketAsyncEventArgs e) |
|||
{ |
|||
var readContext = _waitForData.UserToken as ReadContext; |
|||
int rv = DoRead(readContext.Buffer, readContext.Offset, readContext.Count, readContext.FileDescriptors); |
|||
var tcs = readContext.Tcs; |
|||
if (rv >= 0) |
|||
{ |
|||
readContext.Tcs = null; |
|||
tcs.SetResult(rv); |
|||
} |
|||
else |
|||
{ |
|||
int errno = -rv; |
|||
if (errno == EAGAIN) |
|||
{ |
|||
if (!_socket.ReceiveAsync(_waitForData)) |
|||
DataAvailable(null, null); |
|||
} |
|||
else |
|||
{ |
|||
readContext.Tcs = null; |
|||
tcs.SetException(CreateExceptionForErrno(errno)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private Exception CreateExceptionForErrno(int errno) |
|||
{ |
|||
if (errno == EBADF) |
|||
{ |
|||
return new ObjectDisposedException(typeof(Socket).FullName); |
|||
} |
|||
else |
|||
{ |
|||
return new SocketException(errno); |
|||
} |
|||
} |
|||
|
|||
private unsafe int DoRead(byte[] buffer, int offset, int count, List<UnixFd> fileDescriptors) |
|||
{ |
|||
fixed (byte* buf = buffer) |
|||
{ |
|||
do |
|||
{ |
|||
IOVector iov = new IOVector (); |
|||
iov.Base = buf + offset; |
|||
iov.Length = (SizeT)count; |
|||
|
|||
msghdr msg = new msghdr (); |
|||
msg.msg_iov = &iov; |
|||
msg.msg_iovlen = (SizeT)1; |
|||
|
|||
cmsg_fd cm = new cmsg_fd (); |
|||
msg.msg_control = &cm; |
|||
msg.msg_controllen = (SizeT)sizeof (cmsg_fd); |
|||
|
|||
int rv; |
|||
lock (_gate) |
|||
{ |
|||
if (_socketFd == -1) |
|||
{ |
|||
return -EBADF; |
|||
} |
|||
rv = (int)Interop.recvmsg(_socketFd, new IntPtr(&msg), 0); |
|||
} |
|||
if (rv >= 0) |
|||
{ |
|||
if (cm.hdr.cmsg_level == SOL_SOCKET && cm.hdr.cmsg_type == SCM_RIGHTS) |
|||
{ |
|||
int msgFdCount = ((int)cm.hdr.cmsg_len - sizeof(cmsghdr)) / sizeof(int); |
|||
for (int i = 0; i < msgFdCount; i++) |
|||
{ |
|||
fileDescriptors.Add(new UnixFd(cm.fds[i])); |
|||
} |
|||
} |
|||
return rv; |
|||
} |
|||
else |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
if (errno != EINTR) |
|||
{ |
|||
return -errno; |
|||
} |
|||
} |
|||
} while (true); |
|||
} |
|||
} |
|||
|
|||
public unsafe Task<int> ReadAsync(byte[] buffer, int offset, int count, List<UnixFd> fileDescriptors) |
|||
{ |
|||
if (!_supportsFdPassing) |
|||
{ |
|||
var readContext = _receiveData.UserToken as ReadContext; |
|||
TaskCompletionSource<int> tcs = readContext.Tcs ?? new TaskCompletionSource<int>(); |
|||
readContext.Tcs = tcs; |
|||
_receiveData.SetBuffer(buffer, offset, count); |
|||
readContext.FileDescriptors = fileDescriptors; |
|||
if (!_socket.ReceiveAsync(_receiveData)) |
|||
{ |
|||
if (_receiveData.SocketError == SocketError.Success) |
|||
{ |
|||
return Task.FromResult(_receiveData.BytesTransferred); |
|||
} |
|||
else |
|||
{ |
|||
return Task.FromException<int>(new SocketException((int)_receiveData.SocketError)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return tcs.Task; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var readContext = _waitForData.UserToken as ReadContext; |
|||
TaskCompletionSource<int> tcs = readContext.Tcs ?? new TaskCompletionSource<int>(); |
|||
readContext.Tcs = tcs; |
|||
readContext.Buffer = buffer; |
|||
readContext.Offset = offset; |
|||
readContext.Count = count; |
|||
readContext.FileDescriptors = fileDescriptors; |
|||
while (true) |
|||
{ |
|||
if (!_socket.ReceiveAsync(_waitForData)) |
|||
{ |
|||
int rv = DoRead(buffer, offset, count, fileDescriptors); |
|||
if (rv >= 0) |
|||
{ |
|||
return Task.FromResult(rv); |
|||
} |
|||
else |
|||
{ |
|||
int errno = -rv; |
|||
if (errno == EAGAIN) |
|||
{ |
|||
continue; |
|||
} |
|||
else |
|||
{ |
|||
return Task.FromException<int>(CreateExceptionForErrno(errno)); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return tcs.Task; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Task SendAsync(Message message) |
|||
{ |
|||
if (!_supportsFdPassing && message.Header.NumberOfFds > 0) |
|||
{ |
|||
foreach (var unixFd in message.UnixFds) |
|||
{ |
|||
unixFd.SafeHandle.Dispose(); |
|||
} |
|||
message.Header.NumberOfFds = 0; |
|||
message.UnixFds = null; |
|||
} |
|||
|
|||
if (message.UnixFds != null && message.UnixFds.Length > 0) |
|||
{ |
|||
return SendMessageWithFdsAsync(message); |
|||
} |
|||
else |
|||
{ |
|||
_bufferList.Clear(); |
|||
var headerBytes = message.Header.ToArray(); |
|||
_bufferList.Add(new ArraySegment<byte>(headerBytes, 0, headerBytes.Length)); |
|||
if (message.Body != null) |
|||
{ |
|||
_bufferList.Add(new ArraySegment<byte>(message.Body, 0, message.Body.Length)); |
|||
} |
|||
return SendBufferListAsync(_bufferList); |
|||
} |
|||
} |
|||
|
|||
private unsafe int SendMsg(msghdr* msg, int length) |
|||
{ |
|||
// This method does NOT handle splitting msg and EAGAIN
|
|||
do |
|||
{ |
|||
IntPtr rv; |
|||
lock (_gate) |
|||
{ |
|||
if (_socketFd == -1) |
|||
{ |
|||
throw new ObjectDisposedException(typeof(Socket).FullName); |
|||
} |
|||
rv = Interop.sendmsg(_socketFd, new IntPtr(msg), 0); |
|||
} |
|||
if (rv == new IntPtr(length)) |
|||
{ |
|||
return length; |
|||
} |
|||
if (rv == new IntPtr(-1)) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
if (errno != EINTR) |
|||
{ |
|||
return -errno; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return -EAGAIN; |
|||
} |
|||
} while (true); |
|||
} |
|||
|
|||
private unsafe Task SendMessageWithFdsAsync(Message message) |
|||
{ |
|||
var headerBytes = message.Header.ToArray(); |
|||
fixed (byte* bufHeader = headerBytes) |
|||
{ |
|||
fixed (byte* bufBody = message.Body) |
|||
{ |
|||
IOVector* iovs = stackalloc IOVector[2]; |
|||
iovs[0].Base = bufHeader; |
|||
iovs[0].Length = (SizeT)headerBytes.Length; |
|||
iovs[1].Base = bufBody; |
|||
int bodyLength = message.Body?.Length ?? 0; |
|||
iovs[1].Length = (SizeT)bodyLength; |
|||
|
|||
msghdr msg = new msghdr (); |
|||
msg.msg_iov = iovs; |
|||
msg.msg_iovlen = (SizeT)2; |
|||
|
|||
var fdm = new cmsg_fd (); |
|||
int size = sizeof(cmsghdr) + 4 * message.UnixFds.Length; |
|||
msg.msg_control = &fdm; |
|||
msg.msg_controllen = (SizeT)size; |
|||
fdm.hdr.cmsg_len = (SizeT)size; |
|||
fdm.hdr.cmsg_level = SOL_SOCKET; |
|||
fdm.hdr.cmsg_type = SCM_RIGHTS; |
|||
for (int i = 0, j = 0; i < message.UnixFds.Length; i++) |
|||
{ |
|||
fdm.fds[j++] = message.UnixFds[i].Handle; |
|||
} |
|||
|
|||
int rv = SendMsg(&msg, headerBytes.Length + bodyLength); |
|||
|
|||
if (message.UnixFds != null) |
|||
{ |
|||
foreach (var fd in message.UnixFds) |
|||
{ |
|||
fd.SafeHandle.Dispose(); |
|||
} |
|||
} |
|||
if (rv >= 0) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
else |
|||
{ |
|||
var errno = -rv; |
|||
return Task.FromException(CreateExceptionForErrno(errno)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public unsafe Task SendAsync(byte[] buffer, int offset, int count) |
|||
{ |
|||
_bufferList.Clear(); |
|||
_bufferList.Add(new ArraySegment<byte>(buffer, offset, count)); |
|||
return SendBufferListAsync(_bufferList); |
|||
} |
|||
|
|||
private Task SendBufferListAsync(List<ArraySegment<byte>> bufferList) |
|||
{ |
|||
var sendContext = _sendArgs.UserToken as SendContext; |
|||
TaskCompletionSource<object> tcs = sendContext.Tcs ?? new TaskCompletionSource<object>(); |
|||
sendContext.Tcs = tcs; |
|||
_sendArgs.BufferList = bufferList; |
|||
if (!_socket.SendAsync(_sendArgs)) |
|||
{ |
|||
if (_sendArgs.SocketError == SocketError.Success) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
else |
|||
{ |
|||
return Task.FromException(new SocketException((int)_sendArgs.SocketError)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return tcs.Task; |
|||
} |
|||
} |
|||
|
|||
private void SendCompleted(object sender, SocketAsyncEventArgs e) |
|||
{ |
|||
var sendContext = e.UserToken as SendContext; |
|||
var tcs = sendContext.Tcs; |
|||
sendContext.Tcs = null; |
|||
if (e.SocketError == SocketError.Success) |
|||
{ |
|||
tcs.SetResult(null); |
|||
} |
|||
else |
|||
{ |
|||
tcs.SetException(new SocketException((int)e.SocketError)); |
|||
} |
|||
} |
|||
|
|||
private static int GetFd(Socket socket) |
|||
{ |
|||
if (s_handleProperty != null) |
|||
{ |
|||
// netstandard2.0
|
|||
return (int)(IntPtr)s_handleProperty.GetValue(socket, null); |
|||
} |
|||
else if (s_safehandleProperty != null) |
|||
{ |
|||
// .NET Core 1.x
|
|||
return ((SafeHandle)s_safehandleProperty.GetValue(socket, null)).DangerousGetHandle().ToInt32(); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
public static Task<TransportSocket> ConnectAsync(AddressEntry entry, CancellationToken cancellationToken, bool supportsFdPassing) |
|||
{ |
|||
switch (entry.Method) |
|||
{ |
|||
case "tcp": |
|||
return ConnectTcpAsync(entry, cancellationToken); |
|||
case "unix": |
|||
return ConnectUnixAsync(entry, cancellationToken, supportsFdPassing); |
|||
default: |
|||
throw new NotSupportedException("Transport method \"" + entry.Method + "\" not supported"); |
|||
} |
|||
} |
|||
|
|||
private static async Task<TransportSocket> ConnectUnixAsync(AddressEntry entry, CancellationToken cancellationToken, bool supportsFdPassing) |
|||
{ |
|||
Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); |
|||
try |
|||
{ |
|||
var transportSocket = new TransportSocket(socket, supportsFdPassing); |
|||
using (cancellationToken.Register(() => transportSocket.Dispose())) |
|||
{ |
|||
var endpoints = await entry.ResolveAsync().ConfigureAwait(false); |
|||
var endPoint = endpoints[0]; |
|||
|
|||
await transportSocket.ConnectAsync(endPoint).ConfigureAwait(false); |
|||
return transportSocket; |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
socket.Dispose(); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
private static async Task<TransportSocket> ConnectTcpAsync(AddressEntry entry, CancellationToken cancellationToken) |
|||
{ |
|||
var endpoints = await entry.ResolveAsync().ConfigureAwait(false); |
|||
for (int i = 0; i < endpoints.Length; i++) |
|||
{ |
|||
var ipEndPoint = endpoints[i] as IPEndPoint; |
|||
bool lastAddress = i == (endpoints.Length - 1); |
|||
Socket socket = new Socket(ipEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
|||
try |
|||
{ |
|||
var transportSocket = new TransportSocket(socket, supportsFdPassing: false); |
|||
using (cancellationToken.Register(() => transportSocket.Dispose())) |
|||
{ |
|||
await transportSocket.ConnectAsync(ipEndPoint).ConfigureAwait(false); |
|||
return transportSocket; |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
socket.Dispose(); |
|||
if (lastAddress) |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private Task ConnectAsync(EndPoint endPoint) |
|||
=> _socket.ConnectAsync(endPoint); |
|||
} |
|||
} |
|||
@ -1,91 +0,0 @@ |
|||
// Licensed to the .NET Foundation under one or more agreements.
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
|||
// See the LICENSE file in the project root for more information.
|
|||
|
|||
using System.Diagnostics; |
|||
using System; |
|||
using System.Text; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
|
|||
namespace Tmds.DBus.Transports |
|||
{ |
|||
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary>
|
|||
internal sealed class UnixDomainSocketEndPoint : EndPoint |
|||
{ |
|||
private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; |
|||
|
|||
private static readonly Encoding s_pathEncoding = Encoding.UTF8; |
|||
private const int s_nativePathOffset = 2; |
|||
|
|||
private readonly string _path; |
|||
private readonly byte[] _encodedPath; |
|||
|
|||
public UnixDomainSocketEndPoint(string path) |
|||
{ |
|||
if (path == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(path)); |
|||
} |
|||
|
|||
_path = path; |
|||
_encodedPath = s_pathEncoding.GetBytes(_path); |
|||
|
|||
if (path.Length == 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException( |
|||
nameof(path), path, |
|||
string.Format("The path '{0}' is of an invalid length for use with domain sockets on this platform. The length must be at least 1 characters.", path)); |
|||
} |
|||
} |
|||
|
|||
internal UnixDomainSocketEndPoint(SocketAddress socketAddress) |
|||
{ |
|||
if (socketAddress == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(socketAddress)); |
|||
} |
|||
|
|||
if (socketAddress.Family != EndPointAddressFamily) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(socketAddress)); |
|||
} |
|||
|
|||
if (socketAddress.Size > s_nativePathOffset) |
|||
{ |
|||
_encodedPath = new byte[socketAddress.Size - s_nativePathOffset]; |
|||
for (int i = 0; i < _encodedPath.Length; i++) |
|||
{ |
|||
_encodedPath[i] = socketAddress[s_nativePathOffset + i]; |
|||
} |
|||
|
|||
_path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); |
|||
} |
|||
else |
|||
{ |
|||
_encodedPath = Array.Empty<byte>(); |
|||
_path = string.Empty; |
|||
} |
|||
} |
|||
|
|||
public override SocketAddress Serialize() |
|||
{ |
|||
var result = new SocketAddress(AddressFamily.Unix, _encodedPath.Length + s_nativePathOffset); |
|||
|
|||
for (int index = 0; index < _encodedPath.Length; index++) |
|||
{ |
|||
result[s_nativePathOffset + index] = _encodedPath[index]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress); |
|||
|
|||
public override AddressFamily AddressFamily => EndPointAddressFamily; |
|||
|
|||
public string Path => _path; |
|||
|
|||
public override string ToString() => _path; |
|||
} |
|||
} |
|||
@ -1,127 +0,0 @@ |
|||
// Copyright 2016 Tom Deseyn <tom.deseyn@gmail.com>
|
|||
// This software is made available under the MIT License
|
|||
// See COPYING for details
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace Tmds.DBus |
|||
{ |
|||
class WrappedDisposable : IDisposable |
|||
{ |
|||
private object _gate = new object(); |
|||
private bool _disposed; |
|||
private IDisposable _disposable; |
|||
private readonly SynchronizationContext _synchronizationContext; |
|||
|
|||
public WrappedDisposable(SynchronizationContext synchronizationContext) |
|||
{ |
|||
_synchronizationContext = synchronizationContext; |
|||
} |
|||
|
|||
public void Call(Action action) |
|||
{ |
|||
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current) |
|||
{ |
|||
if (_disposed) |
|||
{ |
|||
return; |
|||
} |
|||
_synchronizationContext.Post(_ => |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (!_disposed) |
|||
{ |
|||
action(); |
|||
} |
|||
} |
|||
}, null); |
|||
} |
|||
else |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (!_disposed) |
|||
{ |
|||
action(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Call<T>(Action<T> action, T value, bool disposes = false) |
|||
{ |
|||
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current) |
|||
{ |
|||
if (_disposed) |
|||
{ |
|||
return; |
|||
} |
|||
_synchronizationContext.Post(_ => |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (!_disposed) |
|||
{ |
|||
if (disposes) |
|||
{ |
|||
Dispose(); |
|||
} |
|||
action(value); |
|||
} |
|||
} |
|||
}, null); |
|||
} |
|||
else |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (!_disposed) |
|||
{ |
|||
if (disposes) |
|||
{ |
|||
Dispose(); |
|||
} |
|||
action(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
_disposed = true; |
|||
_disposable?.Dispose(); |
|||
} |
|||
} |
|||
|
|||
public IDisposable Disposable |
|||
{ |
|||
set |
|||
{ |
|||
lock (_gate) |
|||
{ |
|||
if (_disposable != null) |
|||
{ |
|||
throw new InvalidOperationException("Already set"); |
|||
} |
|||
_disposable = value; |
|||
if (_disposed) |
|||
{ |
|||
_disposable.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
public bool IsDisposed |
|||
{ |
|||
get |
|||
{ |
|||
lock (_gate) { return _disposed; } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue