Browse Source

update to latest

feature/linux-automation
Jumar Macato 2 years ago
parent
commit
232aef4d7b
  1. 1
      Avalonia.Desktop.slnf
  2. 7
      Avalonia.sln
  3. 2
      src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
  4. 4
      src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs
  5. 2
      src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs
  6. 21
      src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs
  7. 85
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  8. 36
      src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
  9. 109
      src/Avalonia.FreeDesktop/DBusSystemDialog.cs
  10. 27
      src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs
  11. 8
      src/Linux/Tmds.DBus.SourceGenerator/AccessMode.cs
  12. 34
      src/Linux/Tmds.DBus.SourceGenerator/DBusInterface.cs
  13. 840
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Classes.cs
  14. 791
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Handler.cs
  15. 197
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Parsing.cs
  16. 332
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Proxy.cs
  17. 270
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Utils.cs
  18. 27
      src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.cs
  19. 20
      src/Linux/Tmds.DBus.SourceGenerator/SignatureReader.cs
  20. 8
      src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj
  21. 194
      src/Linux/Tmds.DBus/Address.cs
  22. 301
      src/Linux/Tmds.DBus/AddressEntry.cs
  23. 37
      src/Linux/Tmds.DBus/ArgumentAttribute.cs
  24. 65
      src/Linux/Tmds.DBus/ClientConnectionOptions.cs
  25. 35
      src/Linux/Tmds.DBus/ClientSetupResult.cs
  26. 45
      src/Linux/Tmds.DBus/CloseSafeHandle.cs
  27. 261
      src/Linux/Tmds.DBus/CodeGen/ArgTypeInspector.cs
  28. 23
      src/Linux/Tmds.DBus/CodeGen/ArgumentDescription.cs
  29. 310
      src/Linux/Tmds.DBus/CodeGen/DBusAdapter.cs
  30. 417
      src/Linux/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs
  31. 145
      src/Linux/Tmds.DBus/CodeGen/DBusObjectProxy.cs
  32. 276
      src/Linux/Tmds.DBus/CodeGen/DBusObjectProxyTypeBuilder.cs
  33. 115
      src/Linux/Tmds.DBus/CodeGen/DynamicAssembly.cs
  34. 62
      src/Linux/Tmds.DBus/CodeGen/InterfaceDescription.cs
  35. 39
      src/Linux/Tmds.DBus/CodeGen/MethodDescription.cs
  36. 23
      src/Linux/Tmds.DBus/CodeGen/PropertyDescription.cs
  37. 38
      src/Linux/Tmds.DBus/CodeGen/PropertyTypeInspector.cs
  38. 162
      src/Linux/Tmds.DBus/CodeGen/ReadMethodFactory.cs
  39. 35
      src/Linux/Tmds.DBus/CodeGen/SignalDescription.cs
  40. 79
      src/Linux/Tmds.DBus/CodeGen/TypeBuilderExtensions.cs
  41. 449
      src/Linux/Tmds.DBus/CodeGen/TypeDescription.cs
  42. 23
      src/Linux/Tmds.DBus/CodeGen/TypeExtensions.cs
  43. 157
      src/Linux/Tmds.DBus/CodeGen/WriteMethodFactory.cs
  44. 29
      src/Linux/Tmds.DBus/ConnectException.cs
  45. 1063
      src/Linux/Tmds.DBus/Connection2.cs
  46. 31
      src/Linux/Tmds.DBus/ConnectionInfo.cs
  47. 24
      src/Linux/Tmds.DBus/ConnectionOptions.cs
  48. 23
      src/Linux/Tmds.DBus/ConnectionState.cs
  49. 48
      src/Linux/Tmds.DBus/ConnectionStateChangedEventArgs.cs
  50. 1059
      src/Linux/Tmds.DBus/DBusConnection.cs
  51. 38
      src/Linux/Tmds.DBus/DBusException.cs
  52. 59
      src/Linux/Tmds.DBus/DBusInterfaceAttribute.cs
  53. 15
      src/Linux/Tmds.DBus/DictionaryAttribute.cs
  54. 18
      src/Linux/Tmds.DBus/DisconnectedException.cs
  55. 77
      src/Linux/Tmds.DBus/Environment.cs
  56. 76
      src/Linux/Tmds.DBus/IConnection.cs
  57. 19
      src/Linux/Tmds.DBus/IDBus.cs
  58. 17
      src/Linux/Tmds.DBus/IDBusObject.cs
  59. 52
      src/Linux/Tmds.DBus/Interop.cs
  60. 10
      src/Linux/Tmds.DBus/MessageHandler.cs
  61. 62
      src/Linux/Tmds.DBus/MessageHelper.cs
  62. 179
      src/Linux/Tmds.DBus/ObjectPath2.cs
  63. 76
      src/Linux/Tmds.DBus/OwnerChangedMatchRule.cs
  64. 25
      src/Linux/Tmds.DBus/PropertyAccess.cs
  65. 22
      src/Linux/Tmds.DBus/PropertyAttribute.cs
  66. 85
      src/Linux/Tmds.DBus/PropertyChanges.cs
  67. 35
      src/Linux/Tmds.DBus/Protocol/DType.cs
  68. 14
      src/Linux/Tmds.DBus/Protocol/EndianFlag.cs
  69. 22
      src/Linux/Tmds.DBus/Protocol/FieldCode.cs
  70. 180
      src/Linux/Tmds.DBus/Protocol/Header.cs
  71. 17
      src/Linux/Tmds.DBus/Protocol/HeaderFlag.cs
  72. 16
      src/Linux/Tmds.DBus/Protocol/IMessageStream.cs
  73. 11
      src/Linux/Tmds.DBus/Protocol/IProxyFactory.cs
  74. 163
      src/Linux/Tmds.DBus/Protocol/IntrospectionWriter.cs
  75. 33
      src/Linux/Tmds.DBus/Protocol/Message.cs
  76. 656
      src/Linux/Tmds.DBus/Protocol/MessageReader.cs
  77. 25
      src/Linux/Tmds.DBus/Protocol/MessageType.cs
  78. 608
      src/Linux/Tmds.DBus/Protocol/MessageWriter.cs
  79. 11
      src/Linux/Tmds.DBus/Protocol/MethodHandler.cs
  80. 177
      src/Linux/Tmds.DBus/Protocol/ProtocolInformation.cs
  81. 10
      src/Linux/Tmds.DBus/Protocol/SignalHandler.cs
  82. 26
      src/Linux/Tmds.DBus/Protocol/UnixFd.cs
  83. 21
      src/Linux/Tmds.DBus/ProtocolException.cs
  84. 13
      src/Linux/Tmds.DBus/ReleaseNameReply.cs
  85. 17
      src/Linux/Tmds.DBus/RequestNameOptions.cs
  86. 14
      src/Linux/Tmds.DBus/RequestNameReply.cs
  87. 63
      src/Linux/Tmds.DBus/ServerConnectionOptions.cs
  88. 22
      src/Linux/Tmds.DBus/ServerStartOptions.cs
  89. 45
      src/Linux/Tmds.DBus/ServiceOwnerChangedEventArgs.cs
  90. 24
      src/Linux/Tmds.DBus/ServiceRegistrationOptions.cs
  91. 17
      src/Linux/Tmds.DBus/ServiceStartResult.cs
  92. 83
      src/Linux/Tmds.DBus/SignalMatchRule.cs
  93. 63
      src/Linux/Tmds.DBus/SignalWatcher.cs
  94. 899
      src/Linux/Tmds.DBus/Signature.cs
  95. 7
      src/Linux/Tmds.DBus/Tmds.DBus.csproj
  96. 191
      src/Linux/Tmds.DBus/Transports/LocalServer.cs
  97. 433
      src/Linux/Tmds.DBus/Transports/Transport.cs
  98. 525
      src/Linux/Tmds.DBus/Transports/TransportSocket.cs
  99. 91
      src/Linux/Tmds.DBus/Transports/UnixDomainSocketEndPoint.cs
  100. 127
      src/Linux/Tmds.DBus/WrappedDisposable.cs

1
Avalonia.Desktop.slnf

@ -41,7 +41,6 @@
"src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
"src\\Linux\\Tmds.DBus.Protocol\\Tmds.DBus.Protocol.csproj",
"src\\Linux\\Tmds.DBus.SourceGenerator\\Tmds.DBus.SourceGenerator.csproj",
"src\\Linux\\Tmds.DBus\\Tmds.DBus.csproj",
"src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
"src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",

7
Avalonia.sln

@ -306,8 +306,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus.Protocol", "src\L
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus.SourceGenerator", "src\Linux\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator.csproj", "{FFE8B040-B467-424A-9DDB-6155DC1EB62E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus", "src\Linux\Tmds.DBus\Tmds.DBus.csproj", "{1ABAB94E-D687-4E3B-8489-39EDCFA91653}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -716,10 +714,6 @@ Global
{FFE8B040-B467-424A-9DDB-6155DC1EB62E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFE8B040-B467-424A-9DDB-6155DC1EB62E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFE8B040-B467-424A-9DDB-6155DC1EB62E}.Release|Any CPU.Build.0 = Release|Any CPU
{1ABAB94E-D687-4E3B-8489-39EDCFA91653}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1ABAB94E-D687-4E3B-8489-39EDCFA91653}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ABAB94E-D687-4E3B-8489-39EDCFA91653}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1ABAB94E-D687-4E3B-8489-39EDCFA91653}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -809,7 +803,6 @@ Global
{9AE1B827-21AC-4063-AB22-C8804B7F931E} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{18B242C7-33BC-4B40-B12C-82B20F2BF638} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{FFE8B040-B467-424A-9DDB-6155DC1EB62E} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{1ABAB94E-D687-4E3B-8489-39EDCFA91653} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

2
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@ -20,8 +20,6 @@
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
PrivateAssets="all"/>
<ProjectReference Include="..\Linux\Tmds.DBus\Tmds.DBus.csproj" />
</ItemGroup>
<ItemGroup>

4
src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs

@ -45,7 +45,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
public ValueTask<IDisposable> WatchForwardKeyAsync(Action<Exception?, (uint keyval, uint state, int type)> handler) =>
_old?.WatchForwardKeyAsync(handler)
?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.keyval, ev.state, ev.type ? 1 : 0)))
?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.Keyval, ev.State, ev.Type ? 1 : 0)))
?? new ValueTask<IDisposable>(Disposable.Empty);
public ValueTask<IDisposable> WatchUpdateFormattedPreeditAsync(
@ -53,7 +53,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
_old?.WatchUpdateFormattedPreeditAsync(handler!)
?? _modern?.WatchUpdateFormattedPreeditAsync(handler!)
?? new ValueTask<IDisposable>(Disposable.Empty);
public Task SetCapacityAsync(uint flags) =>
_old?.SetCapacityAsync(flags) ?? _modern?.SetCapabilityAsync(flags) ?? Task.CompletedTask;
}

2
src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs

@ -29,7 +29,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
var resp = await method.CreateICv3Async(GetAppName(),
Process.GetCurrentProcess().Id);
var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.icid}");
var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.Icid}");
_context = new FcitxICWrapper(proxy);
}
else

21
src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs

@ -51,17 +51,16 @@ namespace Avalonia.FreeDesktop.DBusIme.IBus
Client.SetPreeditText(_preeditText, _preeditText == null ? null : _preeditCursor);
}
private void OnUpdatePreedit(Exception? arg1, (DBusVariantItem text, uint cursor_pos, bool visible) preeditComponents)
private void OnUpdatePreedit(Exception? arg1, (VariantValue Text, uint CursorPos, bool Visible) preeditComponents)
{
if (preeditComponents.text is { Value: DBusStructItem { Count: >= 3 } structItem } &&
structItem[2] is DBusStringItem stringItem)
if (preeditComponents.Text is { Type: VariantValueType.Struct, Count: >= 3 } structItem && structItem.GetItem(2) is { Type: VariantValueType.String} stringItem)
{
_preeditText = stringItem.Value;
_preeditText = stringItem.GetString();
_preeditCursor = _preeditText != null
? Utf16Utils.CharacterOffsetToStringOffset(_preeditText,
(int)Math.Min(preeditComponents.cursor_pos, int.MaxValue), false)
(int)Math.Min(preeditComponents.CursorPos, int.MaxValue), false)
: 0;
_preeditShown = true;
}
else
@ -102,13 +101,13 @@ namespace Avalonia.FreeDesktop.DBusIme.IBus
});
}
private void OnCommitText(Exception? e, DBusVariantItem variantItem)
private void OnCommitText(Exception? e, VariantValue variantItem)
{
if (_insideReset > 0)
if (_insideReset > 0)
{
// For some reason iBus can trigger a CommitText while being reset.
// Thankfully the signal is sent _during_ Reset call processing,
// so it arrives on-the-wire before Reset call result, so we can
// so it arrives on-the-wire before Reset call result, so we can
// check if we have any pending Reset calls and ignore the signal here
return;
}
@ -118,8 +117,8 @@ namespace Avalonia.FreeDesktop.DBusIme.IBus
return;
}
if (variantItem.Value is DBusStructItem { Count: >= 3 } structItem && structItem[2] is DBusStringItem stringItem)
FireCommit(stringItem.Value);
if (variantItem.Count >= 3 && variantItem.GetItem(2) is { Type: VariantValueType.String } stringItem)
FireCommit(stringItem.GetString());
}
protected override Task DisconnectAsync() => _service?.DestroyAsync() ?? Task.CompletedTask;

85
src/Avalonia.FreeDesktop/DBusMenuExporter.cs

@ -60,17 +60,14 @@ namespace Avalonia.FreeDesktop
private void InitBackingProperties()
{
BackingProperties.Version = 4;
BackingProperties.Status = string.Empty;
BackingProperties.TextDirection = string.Empty;
BackingProperties.IconThemePath = Array.Empty<string>();
Version = 4;
}
protected override Connection Connection { get; }
public override string Path { get; }
protected override ValueTask<(uint revision, (int, Dictionary<string, DBusVariantItem>, DBusVariantItem[]) layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames)
protected override ValueTask<(uint Revision, (int, Dictionary<string, Variant>, Variant[]) Layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames)
{
var menu = GetMenu(parentId);
var layout = GetLayout(menu.item, menu.menu, recursionDepth, propertyNames);
@ -80,22 +77,22 @@ namespace Avalonia.FreeDesktop
OnIsNativeMenuExportedChanged?.Invoke(this, EventArgs.Empty);
}
return new ValueTask<(uint, (int, Dictionary<string, DBusVariantItem>, DBusVariantItem[]))>((_revision, layout));
return new ValueTask<(uint, (int, Dictionary<string, Variant>, Variant[]))>((_revision, layout));
}
protected override ValueTask<(int, Dictionary<string, DBusVariantItem>)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames)
protected override ValueTask<(int, Dictionary<string, Variant>)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames)
=> new(ids.Select(id => (id, GetProperties(GetMenu(id), propertyNames))).ToArray());
protected override ValueTask<DBusVariantItem> OnGetPropertyAsync(int id, string name) =>
new(GetProperty(GetMenu(id), name) ?? new DBusVariantItem("i", new DBusInt32Item(0)));
protected override ValueTask<Variant> OnGetPropertyAsync(int id, string name) =>
new(GetProperty(GetMenu(id), name) ?? new Variant(0));
protected override ValueTask OnEventAsync(int id, string eventId, DBusVariantItem data, uint timestamp)
protected override ValueTask OnEventAsync(int id, string eventId, VariantValue data, uint timestamp)
{
HandleEvent(id, eventId);
return new ValueTask();
}
protected override ValueTask<int[]> OnEventGroupAsync((int, string, DBusVariantItem, uint)[] events)
protected override ValueTask<int[]> OnEventGroupAsync((int, string, VariantValue, uint)[] events)
{
foreach (var e in events)
HandleEvent(e.Item1, e.Item2);
@ -104,7 +101,7 @@ namespace Avalonia.FreeDesktop
protected override ValueTask<bool> OnAboutToShowAsync(int id) => new(false);
protected override ValueTask<(int[] updatesNeeded, int[] idErrors)> OnAboutToShowGroupAsync(int[] ids) =>
protected override ValueTask<(int[] UpdatesNeeded, int[] IdErrors)> OnAboutToShowGroupAsync(int[] ids) =>
new((Array.Empty<int>(), Array.Empty<int>()));
private async Task InitializeAsync()
@ -220,35 +217,32 @@ namespace Avalonia.FreeDesktop
"type", "label", "enabled", "visible", "shortcut", "toggle-type", "children-display", "toggle-state", "icon-data"
};
private static DBusVariantItem? GetProperty((NativeMenuItemBase? item, NativeMenu? menu) i, string name)
private static Variant? GetProperty((NativeMenuItemBase? item, NativeMenu? menu) i, string name)
{
var (it, menu) = i;
if (it is NativeMenuItemSeparator)
{
if (name == "type")
return new DBusVariantItem("s", new DBusStringItem("separator"));
return new Variant("separator");
}
else if (it is NativeMenuItem item)
{
if (name == "type")
return null;
if (name == "label")
return new DBusVariantItem("s", new DBusStringItem(item.Header ?? "<null>"));
return new Variant(item.Header ?? "<null>");
if (name == "enabled")
{
if (item.Menu is not null && item.Menu.Items.Count == 0)
return new DBusVariantItem("b", new DBusBoolItem(false));
return new Variant(false);
if (!item.IsEnabled)
return new DBusVariantItem("b", new DBusBoolItem(false));
return new Variant(false);
return null;
}
if (name == "visible") {
if (!item.IsVisible)
return new DBusVariantItem("b", new DBusBoolItem(false));
return new DBusVariantItem("b", new DBusBoolItem(true));
}
if (name == "visible")
return new Variant(item.IsVisible);
if (name == "shortcut")
{
@ -256,30 +250,30 @@ namespace Avalonia.FreeDesktop
return null;
if (item.Gesture.KeyModifiers == 0)
return null;
var lst = new List<DBusItem>();
var lst = new Array<Variant>();
var mod = item.Gesture;
if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Control))
lst.Add(new DBusStringItem("Control"));
lst.Add(new Variant("Control"));
if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Alt))
lst.Add(new DBusStringItem("Alt"));
lst.Add(new Variant("Alt"));
if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Shift))
lst.Add(new DBusStringItem("Shift"));
lst.Add(new Variant("Shift"));
if (mod.KeyModifiers.HasAllFlags(KeyModifiers.Meta))
lst.Add(new DBusStringItem("Super"));
lst.Add(new DBusStringItem(item.Gesture.Key.ToString()));
return new DBusVariantItem("aas", new DBusArrayItem(DBusType.Array, new[] { new DBusArrayItem(DBusType.String, lst) }));
lst.Add(new Variant("Super"));
lst.Add(new Variant(item.Gesture.Key.ToString()));
return Variant.FromArray(new Array<Array<Variant>>(new[] { lst }));
}
if (name == "toggle-type")
{
if (item.ToggleType == NativeMenuItemToggleType.CheckBox)
return new DBusVariantItem("s", new DBusStringItem("checkmark"));
return new Variant("checkmark");
if (item.ToggleType == NativeMenuItemToggleType.Radio)
return new DBusVariantItem("s", new DBusStringItem("radio"));
return new Variant("radio");
}
if (name == "toggle-state" && item.ToggleType != NativeMenuItemToggleType.None)
return new DBusVariantItem("i", new DBusInt32Item(item.IsChecked ? 1 : 0));
return new Variant(item.IsChecked ? 1 : 0);
if (name == "icon-data")
{
@ -292,50 +286,49 @@ namespace Avalonia.FreeDesktop
var icon = loader.LoadIcon(item.Icon.PlatformImpl.Item);
using var ms = new MemoryStream();
icon.Save(ms);
return new DBusVariantItem("ay", new DBusByteArrayItem(ms.ToArray()));
return Variant.FromArray(new Array<byte>(ms.ToArray()));
}
}
}
if (name == "children-display")
return menu is not null ? new DBusVariantItem("s", new DBusStringItem("submenu")) : null;
{
if (menu is not null)
return new Variant("submenu");
return null;
}
}
return null;
}
private static Dictionary<string, DBusVariantItem> GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names)
private static Dictionary<string, Variant> GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names)
{
if (names.Length == 0)
names = s_allProperties;
var properties = new Dictionary<string, DBusVariantItem>();
var properties = new Dictionary<string, Variant>();
foreach (var n in names)
{
var v = GetProperty(i, n);
if (v is not null)
properties.Add(n, v);
if (v.HasValue)
properties.Add(n, v.Value);
}
return properties;
}
private (int, Dictionary<string, DBusVariantItem>, DBusVariantItem[]) GetLayout(NativeMenuItemBase? item, NativeMenu? menu, int depth, string[] propertyNames)
private (int, Dictionary<string, Variant>, Variant[]) GetLayout(NativeMenuItemBase? item, NativeMenu? menu, int depth, string[] propertyNames)
{
var id = item is null ? 0 : GetId(item);
var props = GetProperties((item, menu), propertyNames);
var children = depth == 0 || menu is null ? Array.Empty<DBusVariantItem>() : new DBusVariantItem[menu.Items.Count];
var children = depth == 0 || menu is null ? Array.Empty<Variant>() : new Variant[menu.Items.Count];
if (menu is not null)
{
for (var c = 0; c < children.Length; c++)
{
var ch = menu.Items[c];
var layout = GetLayout(ch, (ch as NativeMenuItem)?.Menu, depth == -1 ? -1 : depth - 1, propertyNames);
children[c] = new DBusVariantItem("(ia{sv}av)", new DBusStructItem(new DBusItem[]
{
new DBusInt32Item(layout.Item1),
new DBusArrayItem(DBusType.DictEntry, layout.Item2.Select(static x => new DBusDictEntryItem(new DBusStringItem(x.Key), x.Value)).ToArray()),
new DBusArrayItem(DBusType.Variant, layout.Item3)
}));
children[c] = Variant.FromStruct(Struct.Create(layout.Item1, new Dict<string, Variant>(layout.Item2), new Array<Variant>(layout.Item3)));
}
}

36
src/Avalonia.FreeDesktop/DBusPlatformSettings.cs

@ -41,14 +41,12 @@ namespace Avalonia.FreeDesktop
try
{
var version = await _settings!.GetVersionPropertyAsync();
DBusVariantItem value;
VariantValue value;
if (version >= 2)
value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "color-scheme");
else
value = (DBusVariantItem)(await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme")).Value;
if (value.Value is DBusUInt32Item dBusUInt32Item)
return ToColorScheme(dBusUInt32Item.Value);
return null;
value = (await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme")).GetItem(0);
return ToColorScheme(value.GetUInt32());
}
catch (DBusException)
{
@ -61,14 +59,12 @@ namespace Avalonia.FreeDesktop
try
{
var version = await _settings!.GetVersionPropertyAsync();
DBusVariantItem value;
VariantValue value;
if (version >= 2)
value = await _settings!.ReadOneAsync("org.freedesktop.appearance", "accent-color");
else
value = (DBusVariantItem)(await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color")).Value;
if (value.Value is DBusStructItem dBusStructItem)
return ToAccentColor(dBusStructItem);
return null;
value = (await _settings!.ReadAsync("org.freedesktop.appearance", "accent-color")).GetItem(0);
return ToAccentColor(value);
}
catch (DBusException)
{
@ -76,20 +72,20 @@ namespace Avalonia.FreeDesktop
}
}
private void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple)
private void SettingsChangedHandler(Exception? exception, (string Namespace, string Key, VariantValue Value) tuple)
{
if (exception is not null)
return;
switch (valueTuple)
switch (tuple)
{
case ("org.freedesktop.appearance", "color-scheme", { } colorScheme):
_themeVariant = ToColorScheme((colorScheme.Value as DBusUInt32Item)!.Value);
case ("org.freedesktop.appearance", "color-scheme", var colorScheme):
_themeVariant = ToColorScheme(colorScheme.GetUInt32());
_lastColorValues = BuildPlatformColorValues();
OnColorValuesChanged(_lastColorValues!);
break;
case ("org.freedesktop.appearance", "accent-color", { } accentColor):
_accentColor = ToAccentColor((accentColor.Value as DBusStructItem)!);
case ("org.freedesktop.appearance", "accent-color", var accentColor):
_accentColor = ToAccentColor(accentColor);
_lastColorValues = BuildPlatformColorValues();
OnColorValuesChanged(_lastColorValues!);
break;
@ -118,16 +114,16 @@ namespace Avalonia.FreeDesktop
return isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light;
}
private static Color? ToAccentColor(DBusStructItem value)
private static Color? ToAccentColor(VariantValue value)
{
/*
Indicates the system's preferred accent color as a tuple of RGB values
in the sRGB color space, in the range [0,1].
Out-of-range RGB values should be treated as an unset accent color.
*/
var r = (value[0] as DBusDoubleItem)!.Value;
var g = (value[1] as DBusDoubleItem)!.Value;
var b = (value[2] as DBusDoubleItem)!.Value;
var r = value.GetItem(0).GetDouble();
var g = value.GetItem(1).GetDouble();
var b = value.GetItem(2).GetDouble();
if (r is < 0 or > 1 || g is < 0 or > 1 || b is < 0 or > 1)
return null;
return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255));

109
src/Avalonia.FreeDesktop/DBusSystemDialog.cs

@ -56,14 +56,15 @@ namespace Avalonia.FreeDesktop
{
var parentWindow = $"x11:{_handle.Handle:X}";
ObjectPath objectPath;
var chooserOptions = new Dictionary<string, DBusVariantItem>();
var filters = ParseFilters(options.FileTypeFilter);
if (filters is not null)
var chooserOptions = new Dictionary<string, Variant>();
if (TryParseFilters(options.FileTypeFilter, out var filters))
chooserOptions.Add("filters", filters);
if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0"))));
chooserOptions.Add("multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple)));
chooserOptions.Add("current_folder", Variant.FromArray(new Array<byte>(Encoding.UTF8.GetBytes(folderPath + "\0"))));
chooserOptions.Add("multiple", new Variant(options.AllowMultiple));
objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
@ -74,7 +75,7 @@ namespace Avalonia.FreeDesktop
if (e is not null)
tsc.TrySetException(e);
else
tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray());
tsc.TrySetResult(x.Results["uris"].GetArray<string>());
});
var uris = await tsc.Task ?? Array.Empty<string>();
@ -85,15 +86,14 @@ namespace Avalonia.FreeDesktop
{
var parentWindow = $"x11:{_handle.Handle:X}";
ObjectPath objectPath;
var chooserOptions = new Dictionary<string, DBusVariantItem>();
var filters = ParseFilters(options.FileTypeChoices);
if (filters is not null)
var chooserOptions = new Dictionary<string, Variant>();
if (TryParseFilters(options.FileTypeChoices, out var filters))
chooserOptions.Add("filters", filters);
if (options.SuggestedFileName is { } currentName)
chooserOptions.Add("current_name", new DBusVariantItem("s", new DBusStringItem(currentName)));
chooserOptions.Add("current_name", new Variant(currentName));
if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0"))));
chooserOptions.Add("current_folder", Variant.FromArray(new Array<byte>(Encoding.UTF8.GetBytes(folderPath + "\0"))));
objectPath = await _fileChooser.SaveFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
@ -102,41 +102,31 @@ namespace Avalonia.FreeDesktop
using var disposable = await request.WatchResponseAsync((e, x) =>
{
if (e is not null)
{
tsc.TrySetException(e);
}
else
{
if(x.results.TryGetValue("current_filter", out var value))
if (x.Results.TryGetValue("current_filter", out var currentFilter))
{
var currentFilter = value.Value as DBusStructItem;
if(currentFilter != null)
var name = currentFilter.GetItem(0).GetString();
selectedType = new FilePickerFileType(name);
var patterns = new List<string>();
var mimeTypes = new List<string>();
var types = currentFilter.GetItem(1).GetArray<VariantValue>();
foreach(var t in types)
{
var name = (currentFilter[0] as DBusStringItem)?.Value.ToString() ?? "";
selectedType = new FilePickerFileType(name);
if(currentFilter[1] is DBusArrayItem types)
{
List<string> filters = new List<string>();
List<string> mimeTypes = new List<string>();
foreach(var t in types)
{
if(t is DBusStructItem filter)
{
if((filter[0] as DBusUInt32Item)?.Value == 1)
{
mimeTypes.Add((filter[1] as DBusStringItem)?.Value.ToString() ?? "");
}
else
{
filters.Add((filter[1] as DBusStringItem)?.Value.ToString() ?? "");
}
}
}
selectedType.Patterns = filters;
selectedType.MimeTypes = mimeTypes;
}
if (t.GetItem(0).GetUInt32() == 1)
mimeTypes.Add(t.GetItem(1).GetString());
else
patterns.Add(t.GetItem(1).GetString());
}
selectedType.Patterns = patterns;
selectedType.MimeTypes = mimeTypes;
}
tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray());
tsc.TrySetResult(x.Results["uris"].GetArray<string>());
}
});
@ -157,16 +147,16 @@ namespace Avalonia.FreeDesktop
return Array.Empty<IStorageFolder>();
var parentWindow = $"x11:{_handle.Handle:X}";
var chooserOptions = new Dictionary<string, DBusVariantItem>
var chooserOptions = new Dictionary<string, Variant>
{
{ "directory", new DBusVariantItem("b", new DBusBoolItem(true)) },
{ "multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple)) }
{ "directory", new Variant(true) },
{ "multiple", new Variant(options.AllowMultiple) }
};
if (options.SuggestedFileName is { } currentName)
chooserOptions.Add("current_name", new DBusVariantItem("s", new DBusStringItem(currentName)));
chooserOptions.Add("current_name", new Variant(currentName));
if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0"))));
chooserOptions.Add("current_folder", Variant.FromArray(new Array<byte>(Encoding.UTF8.GetBytes(folderPath + "\0"))));
var objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
@ -176,7 +166,7 @@ namespace Avalonia.FreeDesktop
if (e is not null)
tsc.TrySetException(e);
else
tsc.TrySetResult((x.results["uris"].Value as DBusArrayItem)?.Select(static y => (y as DBusStringItem)!.Value).ToArray());
tsc.TrySetResult(x.Results["uris"].GetArray<string>());
});
var uris = await tsc.Task ?? Array.Empty<string>();
@ -187,40 +177,35 @@ namespace Avalonia.FreeDesktop
.Select(static path => new BclStorageFolder(new DirectoryInfo(path))).ToList();
}
private static DBusVariantItem? ParseFilters(IReadOnlyList<FilePickerFileType>? fileTypes)
private static bool TryParseFilters(IReadOnlyList<FilePickerFileType>? fileTypes, out Variant result)
{
const uint GlobStyle = 0u;
const uint MimeStyle = 1u;
// Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])]
if (fileTypes is null)
return null;
{
result = default;
return false;
}
var filters = new List<DBusItem>();
var filters = new Array<Struct<string, Array<Struct<uint, string>>>>();
foreach (var fileType in fileTypes)
{
var extensions = new List<DBusItem>();
var extensions = new List<Struct<uint, string>>();
if (fileType.Patterns?.Count > 0)
extensions.AddRange(
fileType.Patterns.Select(static pattern =>
new DBusStructItem(new DBusItem[] { new DBusUInt32Item(GlobStyle), new DBusStringItem(pattern) })));
extensions.AddRange(fileType.Patterns.Select(static pattern => Struct.Create(GlobStyle, pattern)));
else if (fileType.MimeTypes?.Count > 0)
extensions.AddRange(
fileType.MimeTypes.Select(static mimeType =>
new DBusStructItem(new DBusItem[] { new DBusUInt32Item(MimeStyle), new DBusStringItem(mimeType) })));
extensions.AddRange(fileType.MimeTypes.Select(static mimeType => Struct.Create(MimeStyle, mimeType)));
else
continue;
filters.Add(new DBusStructItem(
new DBusItem[]
{
new DBusStringItem(fileType.Name),
new DBusArrayItem(DBusType.Struct, extensions)
}));
filters.Add(Struct.Create(fileType.Name, new Array<Struct<uint, string>>(extensions)));
}
return filters.Count > 0 ? new DBusVariantItem("a(sa(us))", new DBusArrayItem(DBusType.Struct, filters)) : null;
result = Variant.FromArray(filters);
return true;
}
}
}

27
src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs

@ -82,7 +82,7 @@ namespace Avalonia.FreeDesktop
if (_isDisposed || _connection is null || name != "org.kde.StatusNotifierWatcher")
return;
if (!_serviceConnected & newOwner is not null)
if (!_serviceConnected && newOwner is not null)
{
_serviceConnected = true;
_statusNotifierWatcher = new OrgKdeStatusNotifierWatcher(_connection, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher");
@ -214,18 +214,7 @@ namespace Avalonia.FreeDesktop
public StatusNotifierItemDbusObj(Connection connection, ObjectPath dbusMenuPath)
{
Connection = connection;
BackingProperties.Menu = dbusMenuPath;
BackingProperties.Category = string.Empty;
BackingProperties.Status = string.Empty;
BackingProperties.Id = string.Empty;
BackingProperties.Title = string.Empty;
BackingProperties.IconPixmap = Array.Empty<(int, int, byte[])>();
BackingProperties.AttentionIconName = string.Empty;
BackingProperties.AttentionIconPixmap = Array.Empty<(int, int, byte[])>();
BackingProperties.AttentionMovieName = string.Empty;
BackingProperties.OverlayIconName = string.Empty;
BackingProperties.OverlayIconPixmap = Array.Empty<(int, int, byte[])>();
BackingProperties.ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), string.Empty, string.Empty);
Menu = dbusMenuPath;
InvalidateAll();
}
@ -254,12 +243,12 @@ namespace Avalonia.FreeDesktop
EmitNewAttentionIcon();
EmitNewOverlayIcon();
EmitNewToolTip();
EmitNewStatus(BackingProperties.Status);
EmitNewStatus(Status);
}
public void SetIcon((int, int, byte[]) dbusPixmap)
{
BackingProperties.IconPixmap = new[] { dbusPixmap };
IconPixmap = new[] { dbusPixmap };
InvalidateAll();
}
@ -268,10 +257,10 @@ namespace Avalonia.FreeDesktop
if (text is null)
return;
BackingProperties.Id = text;
BackingProperties.Category = "ApplicationStatus";
BackingProperties.Status = text;
BackingProperties.Title = text;
Id = text;
Category = "ApplicationStatus";
Status = text;
Title = text;
InvalidateAll();
}
}

8
src/Linux/Tmds.DBus.SourceGenerator/AccessMode.cs

@ -0,0 +1,8 @@
namespace Tmds.DBus.SourceGenerator
{
public enum AccessMode
{
Read,
Write
}
}

34
src/Linux/Tmds.DBus.SourceGenerator/DBusInterface.cs

@ -81,12 +81,6 @@ namespace Tmds.DBus.SourceGenerator
[XmlType(AnonymousType = true)]
public class DBusValue
{
[XmlIgnore]
private string? _dotNetType;
[XmlIgnore]
private string[]? _innerDotNetTypes;
[XmlIgnore]
private DBusType _dBusType;
@ -99,30 +93,6 @@ namespace Tmds.DBus.SourceGenerator
[XmlAttribute("type")]
public string? Type { get; set; }
[XmlIgnore]
public string DotNetType
{
get
{
if (_dotNetType is not null)
return _dotNetType;
(_dotNetType, _innerDotNetTypes, _, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
return _dotNetType;
}
}
[XmlIgnore]
public string[]? InnerDotNetTypes
{
get
{
if (_innerDotNetTypes is not null)
return _innerDotNetTypes;
(_dotNetType, _innerDotNetTypes, _, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
return _innerDotNetTypes;
}
}
[XmlIgnore]
public DBusType DBusType
{
@ -130,7 +100,7 @@ namespace Tmds.DBus.SourceGenerator
{
if (_dBusType != DBusType.Invalid)
return _dBusType;
(_dotNetType, _innerDotNetTypes, _, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
(_, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
return _dBusType;
}
}
@ -142,7 +112,7 @@ namespace Tmds.DBus.SourceGenerator
{
if (_innerDBusTypes is not null)
return _innerDBusTypes;
(_dotNetType, _innerDotNetTypes, _, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
(_, _innerDBusTypes, _dBusType) = DBusSourceGenerator.ParseDBusValue(Type!);
return _innerDBusTypes;
}
}

840
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Classes.cs

@ -8,609 +8,245 @@ namespace Tmds.DBus.SourceGenerator
{
public partial class DBusSourceGenerator
{
private static CompilationUnitSyntax MakePropertyChangesClass() => MakeCompilationUnit(
NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(
RecordDeclaration(Token(SyntaxKind.RecordKeyword), "PropertyChanges")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddTypeParameterListParameters(TypeParameter(Identifier("TProperties")))
.AddParameterListParameters(
Parameter(Identifier("Properties"))
.WithType(IdentifierName("TProperties")),
Parameter(Identifier("Invalidated"))
.WithType(ArrayType(PredefinedType(Token(SyntaxKind.StringKeyword))).AddRankSpecifiers(ArrayRankSpecifier())),
Parameter(Identifier("Changed"))
.WithType(ArrayType(PredefinedType(Token(SyntaxKind.StringKeyword))).AddRankSpecifiers(ArrayRankSpecifier())))
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
.AddMembers(
MethodDeclaration(PredefinedType(Token(SyntaxKind.BoolKeyword)), Identifier("HasChanged"))
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("property")).WithType(PredefinedType(Token(SyntaxKind.StringKeyword))))
.WithExpressionBody(
ArrowExpressionClause(
BinaryExpression(SyntaxKind.NotEqualsExpression,
InvocationExpression(
MakeMemberAccessExpression("Array", "IndexOf"))
.AddArgumentListArguments(
Argument(IdentifierName("Changed")),
Argument(IdentifierName("property"))),
MakeLiteralExpression(-1))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
MethodDeclaration(PredefinedType(Token(SyntaxKind.BoolKeyword)), Identifier("IsInvalidated"))
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("property")).WithType(PredefinedType(Token(SyntaxKind.StringKeyword))))
.WithExpressionBody(
ArrowExpressionClause(
BinaryExpression(SyntaxKind.NotEqualsExpression,
InvocationExpression(
MakeMemberAccessExpression("Array", "IndexOf"))
.AddArgumentListArguments(
Argument(IdentifierName("Invalidated")),
Argument(IdentifierName("property"))),
MakeLiteralExpression(-1))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)))
.WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken))));
private static CompilationUnitSyntax MakeSignalHelperClass()
{
MethodDeclarationSyntax watchSignalMethod = MethodDeclaration(ParseTypeName("ValueTask<IDisposable>"), "WatchSignalAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("connection"))
.WithType(ParseTypeName("Connection")),
Parameter(Identifier("rule"))
.WithType(ParseTypeName("MatchRule")),
Parameter(Identifier("handler"))
.WithType(ParseTypeName("Action<Exception?>")),
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.TrueLiteralExpression))))
.WithBody(
Block(
ReturnStatement(
InvocationExpression(
MakeMemberAccessExpression("connection", "AddMatchAsync"))
.AddArgumentListArguments(
Argument(IdentifierName("rule")),
Argument(
ParenthesizedLambdaExpression()
.AddModifiers(Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("_")),
Parameter(Identifier("_")))
.WithExpressionBody(
PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression,
LiteralExpression(SyntaxKind.NullLiteralExpression)))),
Argument(
ParenthesizedLambdaExpression()
.AddModifiers(Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("e"))
.WithType(ParseTypeName("Exception")),
Parameter(Identifier("_"))
.WithType(PredefinedType(Token(SyntaxKind.ObjectKeyword))),
Parameter(Identifier("_"))
.WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))),
Parameter(Identifier("handlerState"))
.WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithExpressionBody(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParenthesizedExpression(
CastExpression(ParseTypeName("Action<Exception?>"),
PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression,
IdentifierName("handlerState")))),
IdentifierName("Invoke")))
.AddArgumentListArguments(
Argument(IdentifierName("e"))))),
Argument(LiteralExpression(SyntaxKind.NullLiteralExpression)),
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext"))))));
MethodDeclarationSyntax watchSignalWithReadMethod = MethodDeclaration(ParseTypeName("ValueTask<IDisposable>"), "WatchSignalAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddTypeParameterListParameters(
TypeParameter("T"))
private static MethodDeclarationSyntax MakeWriteNullableStringMethod() =>
MethodDeclaration(
PredefinedType(Token(SyntaxKind.VoidKeyword)),
"WriteNullableString")
.AddModifiers(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("connection"))
.WithType(ParseTypeName("Connection")),
Parameter(Identifier("rule"))
.WithType(ParseTypeName("MatchRule")),
Parameter(Identifier("reader"))
.WithType(ParseTypeName("MessageValueReader<T>")),
Parameter(Identifier("handler"))
.WithType(ParseTypeName("Action<Exception?, T>")),
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.TrueLiteralExpression))))
.WithBody(
Block(
ReturnStatement(
InvocationExpression(
MakeMemberAccessExpression("connection", "AddMatchAsync"))
.AddArgumentListArguments(
Argument(IdentifierName("rule")),
Argument(IdentifierName("reader")),
Argument(
ParenthesizedLambdaExpression()
.AddModifiers(Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("e")).WithType(ParseTypeName("Exception")),
Parameter(Identifier("arg"))
.WithType(ParseTypeName("T")),
Parameter(Identifier("readerState"))
.WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))),
Parameter(Identifier("handlerState"))
.WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithExpressionBody(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParenthesizedExpression(
CastExpression(ParseTypeName("Action<Exception?, T>"),
PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression,
IdentifierName("handlerState")))),
IdentifierName("Invoke")))
.AddArgumentListArguments(
Argument(IdentifierName("e")),
Argument(IdentifierName("arg"))))),
Argument(LiteralExpression(SyntaxKind.NullLiteralExpression)),
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext"))))));
MethodDeclarationSyntax watchPropertiesMethod = MethodDeclaration(ParseTypeName("ValueTask<IDisposable>"), "WatchPropertiesChangedAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddTypeParameterListParameters(
TypeParameter("T"))
Parameter(
Identifier("writer"))
.WithType(
IdentifierName("MessageWriter"))
.AddModifiers(
Token(SyntaxKind.ThisKeyword),
Token(SyntaxKind.RefKeyword)),
Parameter(
Identifier("value"))
.WithType(
NullableType(
PredefinedType(Token(SyntaxKind.StringKeyword)))))
.WithExpressionBody(
ArrowExpressionClause(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteString"))
.AddArgumentListArguments(
Argument(
BinaryExpression(
SyntaxKind.CoalesceExpression,
IdentifierName("value"),
MakeMemberAccessExpression("string", "Empty"))))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
private static MethodDeclarationSyntax MakeWriteObjectPathSafeMethod() =>
MethodDeclaration(
PredefinedType(Token(SyntaxKind.VoidKeyword)),
"WriteObjectPathSafe")
.AddModifiers(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("connection"))
.WithType(ParseTypeName("Connection")),
Parameter(Identifier("destination"))
.WithType(PredefinedType(Token(SyntaxKind.StringKeyword))),
Parameter(Identifier("path"))
.WithType(PredefinedType(Token(SyntaxKind.StringKeyword))),
Parameter(Identifier("@interface"))
.WithType(PredefinedType(Token(SyntaxKind.StringKeyword))),
Parameter(Identifier("reader"))
.WithType(ParseTypeName("MessageValueReader<PropertyChanges<T>>")),
Parameter(Identifier("handler"))
.WithType(ParseTypeName("Action<Exception?, PropertyChanges<T>>")),
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.TrueLiteralExpression))))
.WithBody(
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MatchRule"))
.AddVariables(
VariableDeclarator("rule")
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(ParseTypeName("MatchRule"))
.WithInitializer(
InitializerExpression(SyntaxKind.ObjectInitializerExpression)
.AddExpressions(
MakeAssignmentExpression(IdentifierName("Type"),
MakeMemberAccessExpression("MessageType", "Signal")),
MakeAssignmentExpression(IdentifierName("Sender"), IdentifierName("destination")),
MakeAssignmentExpression(IdentifierName("Path"), IdentifierName("path")),
MakeAssignmentExpression(IdentifierName("Member"),
MakeLiteralExpression("PropertiesChanged")),
MakeAssignmentExpression(IdentifierName("Interface"),
MakeLiteralExpression("org.freedesktop.DBus.Properties")),
MakeAssignmentExpression(IdentifierName("Arg0"), IdentifierName("@interface")))))))),
ReturnStatement(
InvocationExpression(
IdentifierName("WatchSignalAsync"))
.AddArgumentListArguments(
Argument(IdentifierName("connection")),
Argument(IdentifierName("rule")),
Argument(IdentifierName("reader")),
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext"))))));
return MakeCompilationUnit(
NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(
ClassDeclaration("SignalHelper")
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))
.AddMembers(watchSignalMethod, watchSignalWithReadMethod, watchPropertiesMethod)));
}
private const string VariantExtensions = """
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tmds.DBus.Protocol;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Tmds.DBus.SourceGenerator
{
internal static class VariantReader
{
public static DBusVariantItem ReadDBusVariant(this ref Reader reader)
{
ReadOnlySpan<byte> signature = reader.ReadSignature();
SignatureReader signatureReader = new(signature);
if (!signatureReader.TryRead(out DBusType dBusType, out ReadOnlySpan<byte> innerSignature))
throw new InvalidOperationException("Unable to read empty variant");
return new DBusVariantItem(Encoding.UTF8.GetString(innerSignature.ToArray()), reader.ReadDBusItem(dBusType, innerSignature));
}
private static DBusBasicTypeItem ReadDBusBasicTypeItem(this ref Reader reader, DBusType dBusType) =>
dBusType switch
{
DBusType.Byte => new DBusByteItem(reader.ReadByte()),
DBusType.Bool => new DBusBoolItem(reader.ReadBool()),
DBusType.Int16 => new DBusInt16Item(reader.ReadInt16()),
DBusType.UInt16 => new DBusUInt16Item(reader.ReadUInt16()),
DBusType.Int32 => new DBusInt32Item(reader.ReadInt32()),
DBusType.UInt32 => new DBusUInt32Item(reader.ReadUInt32()),
DBusType.Int64 => new DBusInt64Item(reader.ReadInt64()),
DBusType.UInt64 => new DBusUInt64Item(reader.ReadUInt64()),
DBusType.Double => new DBusDoubleItem(reader.ReadDouble()),
DBusType.String => new DBusStringItem(reader.ReadString()),
DBusType.ObjectPath => new DBusObjectPathItem(reader.ReadObjectPath()),
DBusType.Signature => new DBusSignatureItem(new Signature(reader.ReadSignature().ToString())),
_ => throw new ArgumentOutOfRangeException(nameof(dBusType))
};
private static DBusItem ReadDBusItem(this ref Reader reader, DBusType dBusType, ReadOnlySpan<byte> innerSignature)
{
switch (dBusType)
{
case DBusType.Byte:
return new DBusByteItem(reader.ReadByte());
case DBusType.Bool:
return new DBusBoolItem(reader.ReadBool());
case DBusType.Int16:
return new DBusInt16Item(reader.ReadInt16());
case DBusType.UInt16:
return new DBusUInt16Item(reader.ReadUInt16());
case DBusType.Int32:
return new DBusInt32Item(reader.ReadInt32());
case DBusType.UInt32:
return new DBusUInt32Item(reader.ReadUInt32());
case DBusType.Int64:
return new DBusInt64Item(reader.ReadInt64());
case DBusType.UInt64:
return new DBusUInt64Item(reader.ReadUInt64());
case DBusType.Double:
return new DBusDoubleItem(reader.ReadDouble());
case DBusType.String:
return new DBusStringItem(reader.ReadString());
case DBusType.ObjectPath:
return new DBusObjectPathItem(reader.ReadObjectPath());
case DBusType.Signature:
return new DBusSignatureItem(new Signature(reader.ReadSignature().ToString()));
case DBusType.Array:
{
SignatureReader innerSignatureReader = new(innerSignature);
if (!innerSignatureReader.TryRead(out DBusType innerDBusType, out ReadOnlySpan<byte> innerArraySignature))
throw new InvalidOperationException("Failed to deserialize array item");
List<DBusItem> items = new();
ArrayEnd arrayEnd = reader.ReadArrayStart(innerDBusType);
while (reader.HasNext(arrayEnd))
items.Add(reader.ReadDBusItem(innerDBusType, innerArraySignature));
return new DBusArrayItem(innerDBusType, items);
}
case DBusType.DictEntry:
{
SignatureReader innerSignatureReader = new(innerSignature);
if (!innerSignatureReader.TryRead(out DBusType innerKeyType, out ReadOnlySpan<byte> _) ||
!innerSignatureReader.TryRead(out DBusType innerValueType, out ReadOnlySpan<byte> innerValueSignature))
throw new InvalidOperationException($"Expected 2 inner types for DictEntry, got {Encoding.UTF8.GetString(innerSignature.ToArray())}");
DBusBasicTypeItem key = reader.ReadDBusBasicTypeItem(innerKeyType);
DBusItem value = reader.ReadDBusItem(innerValueType, innerValueSignature);
return new DBusDictEntryItem(key, value);
}
case DBusType.Struct:
{
reader.AlignStruct();
List<DBusItem> items = new();
SignatureReader innerSignatureReader = new(innerSignature);
while (innerSignatureReader.TryRead(out DBusType innerDBusType, out ReadOnlySpan<byte> innerStructSignature))
items.Add(reader.ReadDBusItem(innerDBusType, innerStructSignature));
return new DBusStructItem(items);
}
case DBusType.Variant:
return reader.ReadDBusVariant();
default:
throw new ArgumentOutOfRangeException();
}
}
}
internal static class VariantWriter
{
public static void WriteDBusVariant(this ref MessageWriter writer, DBusVariantItem value)
{
writer.WriteSignature(Encoding.UTF8.GetBytes(value.Signature).AsSpan());
writer.WriteDBusItem(value.Value);
}
public static void WriteDBusItem(this ref MessageWriter writer, DBusItem value)
{
switch (value)
{
case DBusVariantItem variantItem:
writer.WriteDBusVariant(variantItem);
break;
case DBusByteItem byteItem:
writer.WriteByte(byteItem.Value);
break;
case DBusBoolItem boolItem:
writer.WriteBool(boolItem.Value);
break;
case DBusInt16Item int16Item:
writer.WriteInt16(int16Item.Value);
break;
case DBusUInt16Item uInt16Item:
writer.WriteUInt16(uInt16Item.Value);
break;
case DBusInt32Item int32Item:
writer.WriteInt32(int32Item.Value);
break;
case DBusUInt32Item uInt32Item:
writer.WriteUInt32(uInt32Item.Value);
break;
case DBusInt64Item int64Item:
writer.WriteInt64(int64Item.Value);
break;
case DBusUInt64Item uInt64Item:
writer.WriteUInt64(uInt64Item.Value);
break;
case DBusDoubleItem doubleItem:
writer.WriteDouble(doubleItem.Value);
break;
case DBusStringItem stringItem:
writer.WriteString(stringItem.Value);
break;
case DBusObjectPathItem objectPathItem:
writer.WriteObjectPath(objectPathItem.Value);
break;
case DBusSignatureItem signatureItem:
writer.WriteSignature(signatureItem.Value.ToString());
break;
case DBusArrayItem arrayItem:
ArrayStart arrayStart = writer.WriteArrayStart(arrayItem.ArrayType);
foreach (DBusItem item in arrayItem)
writer.WriteDBusItem(item);
writer.WriteArrayEnd(arrayStart);
break;
case DBusDictEntryItem dictEntryItem:
writer.WriteStructureStart();
writer.WriteDBusItem(dictEntryItem.Key);
writer.WriteDBusItem(dictEntryItem.Value);
break;
case DBusStructItem structItem:
writer.WriteStructureStart();
foreach (DBusItem item in structItem)
writer.WriteDBusItem(item);
break;
case DBusByteArrayItem byteArrayItem:
ArrayStart byteArrayStart = writer.WriteArrayStart(DBusType.Byte);
foreach (byte item in byteArrayItem)
writer.WriteByte(item);
writer.WriteArrayEnd(byteArrayStart);
break;
}
}
}
internal abstract class DBusItem { }
internal abstract class DBusBasicTypeItem : DBusItem { }
internal class DBusVariantItem : DBusItem
{
public DBusVariantItem(string signature, DBusItem value)
{
Signature = signature;
Value = value;
}
public string Signature { get; }
public DBusItem Value { get; }
}
internal class DBusByteItem : DBusBasicTypeItem
{
public DBusByteItem(byte value)
{
Value = value;
}
public byte Value { get; }
}
internal class DBusBoolItem : DBusBasicTypeItem
{
public DBusBoolItem(bool value)
{
Value = value;
}
public bool Value { get; }
}
internal class DBusInt16Item : DBusBasicTypeItem
{
public DBusInt16Item(short value)
{
Value = value;
}
public short Value { get; }
}
internal class DBusUInt16Item : DBusBasicTypeItem
{
public DBusUInt16Item(ushort value)
{
Value = value;
}
public ushort Value { get; }
}
internal class DBusInt32Item : DBusBasicTypeItem
{
public DBusInt32Item(int value)
{
Value = value;
}
public int Value { get; }
}
internal class DBusUInt32Item : DBusBasicTypeItem
{
public DBusUInt32Item(uint value)
{
Value = value;
}
public uint Value { get; }
}
internal class DBusInt64Item : DBusBasicTypeItem
{
public DBusInt64Item(long value)
{
Value = value;
}
public long Value { get; }
}
internal class DBusUInt64Item : DBusBasicTypeItem
{
public DBusUInt64Item(ulong value)
{
Value = value;
}
public ulong Value { get; }
}
internal class DBusDoubleItem : DBusBasicTypeItem
{
public DBusDoubleItem(double value)
{
Value = value;
}
public double Value { get; }
}
internal class DBusStringItem : DBusBasicTypeItem
{
public DBusStringItem(string value)
{
Value = value;
}
public string Value { get; }
}
internal class DBusObjectPathItem : DBusBasicTypeItem
{
public DBusObjectPathItem(ObjectPath value)
{
Value = value;
}
public ObjectPath Value { get; }
}
internal class DBusSignatureItem : DBusBasicTypeItem
{
public DBusSignatureItem(Signature value)
{
Value = value;
}
public Signature Value { get; }
}
internal class DBusArrayItem : DBusItem, IReadOnlyList<DBusItem>
{
private readonly IReadOnlyList<DBusItem> _value;
public DBusArrayItem(DBusType arrayType, IReadOnlyList<DBusItem> value)
{
ArrayType = arrayType;
_value = value;
}
public DBusType ArrayType { get; }
public IEnumerator<DBusItem> GetEnumerator() => _value.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_value).GetEnumerator();
public int Count => _value.Count;
public DBusItem this[int index] => _value[index];
}
internal class DBusDictEntryItem : DBusItem
{
public DBusDictEntryItem(DBusBasicTypeItem key, DBusItem value)
{
Key = key;
Value = value;
}
public DBusBasicTypeItem Key { get; }
public DBusItem Value { get; }
}
internal class DBusStructItem : DBusItem, IReadOnlyList<DBusItem>
{
private readonly IReadOnlyList<DBusItem> _value;
public DBusStructItem(IReadOnlyList<DBusItem> value)
{
_value = value;
}
public IEnumerator<DBusItem> GetEnumerator() => _value.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_value).GetEnumerator();
public int Count => _value.Count;
public DBusItem this[int index] => _value[index];
}
internal class DBusByteArrayItem : DBusItem, IReadOnlyList<byte>
{
private readonly IReadOnlyList<byte> _value;
public DBusByteArrayItem(IReadOnlyList<byte> value)
{
_value = value;
}
public IEnumerator<byte> GetEnumerator() => _value.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_value).GetEnumerator();
public int Count => _value.Count;
public byte this[int index] => _value[index];
}
}
""";
Parameter(
Identifier("writer"))
.WithType(
IdentifierName("MessageWriter"))
.AddModifiers(
Token(SyntaxKind.ThisKeyword),
Token(SyntaxKind.RefKeyword)),
Parameter(
Identifier("value"))
.WithType(
IdentifierName("ObjectPath")))
.WithExpressionBody(
ArrowExpressionClause(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteObjectPath"))
.AddArgumentListArguments(
Argument(
InvocationExpression(
MakeMemberAccessExpression("value", "ToString"))))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
private const string SignalHelperClass = """
using Tmds.DBus.Protocol;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Tmds.DBus.SourceGenerator
{
internal static class SignalHelper
{
public static ValueTask<IDisposable> WatchSignalAsync(Connection connection, MatchRule rule, Action<Exception?> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None)
=> connection.AddMatchAsync(rule, static (_, _) => null !, static (Exception? e, object _, object? _, object? handlerState) => ((Action<Exception?>)handlerState!).Invoke(e), null, handler, emitOnCapturedContext, flags);
public static ValueTask<IDisposable> WatchSignalAsync<T>(Connection connection, MatchRule rule, MessageValueReader<T> reader, Action<Exception?, T> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None)
=> connection.AddMatchAsync(rule, reader, static (e, arg, _, handlerState) => ((Action<Exception?, T>)handlerState!).Invoke(e, arg), null, handler, emitOnCapturedContext, flags);
public static ValueTask<IDisposable> WatchPropertiesChangedAsync<T>(Connection connection, string destination, string path, string @interface, MessageValueReader<PropertyChanges<T>> reader, Action<Exception?, PropertyChanges<T>> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None)
{
MatchRule rule = new()
{
Type = MessageType.Signal,
Sender = destination,
Path = path,
Member = "PropertiesChanged",
Interface = "org.freedesktop.DBus.Properties",
Arg0 = @interface
};
return WatchSignalAsync(connection, rule, reader, handler, emitOnCapturedContext, flags);
}
}
}
""";
private const string PropertyChangesClass = """
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Tmds.DBus.SourceGenerator
{
internal record PropertyChanges<TProperties>(TProperties Properties, string[] Invalidated, string[] Changed)
{
public bool HasChanged(string property) => Array.IndexOf(Changed, property) != -1;
public bool IsInvalidated(string property) => Array.IndexOf(Invalidated, property) != -1;
}
}
""";
private const string DBusInterfaceHandlerInterface = """
using Tmds.DBus.Protocol;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Tmds.DBus.SourceGenerator
{
internal interface IDBusInterfaceHandler
{
PathHandler? PathHandler { get; set; }
Connection Connection { get; }
string InterfaceName { get; }
ReadOnlyMemory<byte> IntrospectXml { get; }
void ReplyGetProperty(string name, MethodContext context);
void ReplyGetAllProperties(MethodContext context);
ValueTask ReplyInterfaceRequest(MethodContext context);
}
}
""";
private const string PathHandlerClass = """
using System.Collections;
using Tmds.DBus.Protocol;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Tmds.DBus.SourceGenerator
{
internal class PathHandler : IMethodHandler
{
private readonly bool _runMethodHandlerSynchronously;
private readonly ICollection<IDBusInterfaceHandler> _dbusInterfaces;
public PathHandler(string path, bool runMethodHandlerSynchronously = true)
{
Path = path;
_runMethodHandlerSynchronously = runMethodHandlerSynchronously;
_dbusInterfaces = new List<IDBusInterfaceHandler>();
}
/// <inheritdoc />
public string Path { get; }
/// <inheritdoc />
public async ValueTask HandleMethodAsync(MethodContext context)
{
switch (context.Request.InterfaceAsString)
{
case "org.freedesktop.DBus.Properties":
switch (context.Request.MemberAsString, context.Request.SignatureAsString)
{
case ("Get", "ss"):
{
Reply();
void Reply()
{
Reader reader = context.Request.GetBodyReader();
string @interface = reader.ReadString();
IDBusInterfaceHandler? handler = _dbusInterfaces.FirstOrDefault(x => x.InterfaceName == @interface);
if (handler is null)
return;
string member = reader.ReadString();
handler.ReplyGetProperty(member, context);
}
break;
}
case ("GetAll", "s"):
{
Reply();
void Reply()
{
Reader reader = context.Request.GetBodyReader();
string @interface = reader.ReadString();
IDBusInterfaceHandler? handler = _dbusInterfaces.FirstOrDefault(x => x.InterfaceName == @interface);
if (handler is null)
return;
handler.ReplyGetAllProperties(context);
}
break;
}
}
break;
case "org.freedesktop.DBus.Introspectable":
context.ReplyIntrospectXml(_dbusInterfaces.Select(static x => x.IntrospectXml).ToArray());
break;
}
IDBusInterfaceHandler? handler = _dbusInterfaces.FirstOrDefault(x => x.InterfaceName == context.Request.InterfaceAsString);
if (handler is not null)
await handler.ReplyInterfaceRequest(context);
}
/// <inheritdoc />
public bool RunMethodHandlerSynchronously(Message message) => _runMethodHandlerSynchronously;
/// <inheritdoc />
public void Add(IDBusInterfaceHandler item)
{
item.PathHandler = this;
_dbusInterfaces.Add(item);
}
/// <inheritdoc />
public bool Contains(IDBusInterfaceHandler item) => _dbusInterfaces.Contains(item);
/// <inheritdoc />
public bool Remove(IDBusInterfaceHandler item)
{
item.PathHandler = null;
return _dbusInterfaces.Remove(item);
}
/// <inheritdoc />
public int Count => _dbusInterfaces.Count;
}
}
""";
}
}

791
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Handler.cs

@ -1,4 +1,3 @@
using System;
using System.IO;
using System.Linq;
using System.Xml;
@ -18,20 +17,27 @@ namespace Tmds.DBus.SourceGenerator
private ClassDeclarationSyntax GenerateHandler(DBusInterface dBusInterface)
{
ClassDeclarationSyntax cl = ClassDeclaration(Pascalize(dBusInterface.Name!))
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.AbstractKeyword))
.AddModifiers(
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.AbstractKeyword))
.AddBaseListTypes(
SimpleBaseType(ParseTypeName("IMethodHandler")))
SimpleBaseType(
IdentifierName("IDBusInterfaceHandler")))
.AddMembers(
FieldDeclaration(
VariableDeclaration(NullableType(ParseTypeName("SynchronizationContext")))
.AddVariables(
VariableDeclarator("_synchronizationContext")))
.AddModifiers(Token(SyntaxKind.PrivateKeyword)),
ConstructorDeclaration(Pascalize(dBusInterface.Name!))
.AddModifiers(Token(SyntaxKind.PublicKeyword))
MakePrivateReadOnlyField(
"_synchronizationContext",
NullableType(
IdentifierName("SynchronizationContext"))),
ConstructorDeclaration(
Pascalize(dBusInterface.Name!))
.AddModifiers(
Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
Parameter(
Identifier("emitOnCapturedContext"))
.WithType(
PredefinedType(
Token(SyntaxKind.BoolKeyword)))
.WithDefault(
EqualsValueClause(
LiteralExpression(SyntaxKind.TrueLiteralExpression))))
@ -40,51 +46,39 @@ namespace Tmds.DBus.SourceGenerator
IfStatement(
IdentifierName("emitOnCapturedContext"),
ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("_synchronizationContext"),
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
IdentifierName("_synchronizationContext"),
MakeMemberAccessExpression("SynchronizationContext", "Current")))))),
MakeGetOnlyProperty(ParseTypeName("Connection"), "Connection", Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.AbstractKeyword)),
MakeGetOnlyProperty(PredefinedType(Token(SyntaxKind.StringKeyword)), "Path", Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.AbstractKeyword)));
MethodDeclarationSyntax handleMethod = MethodDeclaration(ParseTypeName("ValueTask"), "HandleMethodAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.AsyncKeyword))
.AddParameterListParameters(
Parameter(Identifier("context"))
.WithType(ParseTypeName("MethodContext")));
SwitchStatementSyntax switchStatement = SwitchStatement(MakeMemberAccessExpression("context", "Request", "InterfaceAsString"));
AddHandlerMethods(ref cl, ref switchStatement, dBusInterface);
AddHandlerSignals(ref cl, dBusInterface);
AddHandlerProperties(ref cl, ref switchStatement, dBusInterface);
AddHandlerIntrospect(ref cl, ref switchStatement, dBusInterface);
if (dBusInterface.Properties?.Length > 0)
{
cl = cl.AddMembers(
MakeGetOnlyProperty(ParseTypeName("Properties"), "BackingProperties", Token(SyntaxKind.PublicKeyword))
MakeGetSetProperty(
NullableType(
IdentifierName("PathHandler")),
"PathHandler",
Token(SyntaxKind.PublicKeyword)),
MakeGetOnlyProperty(
IdentifierName("Connection"),
"Connection",
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.AbstractKeyword)),
MakeGetOnlyProperty(
PredefinedType(
Token(SyntaxKind.StringKeyword)),
"InterfaceName",
Token(SyntaxKind.PublicKeyword))
.WithInitializer(
EqualsValueClause(
InvocationExpression(
ObjectCreationExpression(ParseTypeName("Properties")))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
}
MakeLiteralExpression(dBusInterface.Name!)))
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken)));
cl = cl.AddMembers(
MethodDeclaration(PredefinedType(Token(SyntaxKind.BoolKeyword)), "RunMethodHandlerSynchronously")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("message")).WithType(ParseTypeName("Message")))
.WithExpressionBody(
ArrowExpressionClause(
LiteralExpression(SyntaxKind.TrueLiteralExpression)))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
handleMethod.WithBody(
Block(switchStatement)));
AddHandlerProperties(ref cl, dBusInterface);
AddHandlerIntrospect(ref cl, dBusInterface);
AddHandlerMethods(ref cl, dBusInterface);
AddHandlerSignals(ref cl, dBusInterface);
return cl;
}
private void AddHandlerMethods(ref ClassDeclarationSyntax cl, ref SwitchStatementSyntax sw, DBusInterface dBusInterface)
private void AddHandlerMethods(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
if (dBusInterface.Methods is null)
return;
@ -96,43 +90,38 @@ namespace Tmds.DBus.SourceGenerator
DBusArgument[]? inArgs = dBusMethod.Arguments?.Where(static m => m.Direction is null or "in").ToArray();
DBusArgument[]? outArgs = dBusMethod.Arguments?.Where(static m => m.Direction == "out").ToArray();
SwitchSectionSyntax switchSection = SwitchSection();
if (inArgs?.Length > 0)
{
switchSection = switchSection.AddLabels(
CaseSwitchLabel(
TupleExpression()
.AddArguments(
Argument(MakeLiteralExpression(dBusMethod.Name!)),
Argument(MakeLiteralExpression(ParseSignature(inArgs)!)))));
}
else
{
switchSection = switchSection.AddLabels(
SwitchSectionSyntax switchSection = SwitchSection()
.AddLabels(
CasePatternSwitchLabel(
RecursivePattern()
.WithPositionalPatternClause(
PositionalPatternClause()
.AddSubpatterns(
Subpattern(
ConstantPattern(MakeLiteralExpression("Introspect"))),
ConstantPattern(
MakeLiteralExpression(dBusMethod.Name!))),
Subpattern(
BinaryPattern(SyntaxKind.OrPattern, ConstantPattern(MakeLiteralExpression(string.Empty)),
ConstantPattern(LiteralExpression(SyntaxKind.NullLiteralExpression)))))),
inArgs?.Length > 0
? ConstantPattern(
MakeLiteralExpression(
ParseSignature(inArgs)!))
: BinaryPattern(SyntaxKind.OrPattern,
ConstantPattern(
MakeLiteralExpression(string.Empty)),
ConstantPattern(
LiteralExpression(SyntaxKind.NullLiteralExpression)))))),
Token(SyntaxKind.ColonToken)));
}
BlockSyntax switchSectionBlock = Block();
string abstractMethodName = $"On{Pascalize(dBusMethod.Name!)}Async";
MethodDeclarationSyntax abstractMethod = outArgs?.Length > 0
? MethodDeclaration(ParseTypeName($"ValueTask<{ParseReturnType(outArgs)!}>"), abstractMethodName)
: MethodDeclaration(ParseTypeName("ValueTask"), abstractMethodName);
MethodDeclarationSyntax abstractMethod = MethodDeclaration(
ParseValueTaskReturnType(outArgs, AccessMode.Write), abstractMethodName);
if (inArgs?.Length > 0)
abstractMethod = abstractMethod.WithParameterList(ParseParameterList(inArgs));
abstractMethod = abstractMethod.WithParameterList(
ParseParameterList(inArgs, AccessMode.Read));
abstractMethod = abstractMethod
.AddModifiers(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.AbstractKeyword))
@ -144,30 +133,29 @@ namespace Tmds.DBus.SourceGenerator
{
BlockSyntax readParametersMethodBlock = Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("Reader"))
VariableDeclaration(IdentifierName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(MakeMemberAccessExpression("context", "Request", "GetBodyReader")))))));
SyntaxList<StatementSyntax> argFields = List<StatementSyntax>();
StatementSyntax[] argFields = new StatementSyntax[inArgs.Length];
for (int i = 0; i < inArgs.Length; i++)
{
string identifier = inArgs[i].Name is not null ? SanitizeIdentifier(inArgs[i].Name!) : $"arg{i}";
string identifier = inArgs[i].Name is not null ? SanitizeIdentifier(Camelize(inArgs[i].Name!)) : $"arg{i}";
readParametersMethodBlock = readParametersMethodBlock.AddStatements(
ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(identifier), InvocationExpression(
MakeMemberAccessExpression("reader", GetOrAddReadMethod(inArgs[i]))))));
argFields = argFields.Add(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName(inArgs[i].DotNetType))
.AddVariables(
VariableDeclarator(identifier))));
argFields[i] = LocalDeclarationStatement(
VariableDeclaration(GetDotnetType(inArgs[i], AccessMode.Read))
.AddVariables(
VariableDeclarator(identifier)));
}
switchSectionBlock = switchSectionBlock.AddStatements(argFields.ToArray());
switchSectionBlock = switchSectionBlock.AddStatements(argFields);
switchSectionBlock = switchSectionBlock.AddStatements(
ExpressionStatement(
InvocationExpression(
@ -175,122 +163,36 @@ namespace Tmds.DBus.SourceGenerator
LocalFunctionStatement(
PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("ReadParameters"))
.WithBody(readParametersMethodBlock));
if (outArgs is null || outArgs.Length == 0)
{
switchSectionBlock = switchSectionBlock.AddStatements(
IfStatement(
IsPatternExpression(
IdentifierName("_synchronizationContext"), UnaryPattern(ConstantPattern(LiteralExpression(SyntaxKind.NullLiteralExpression)))),
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("TaskCompletionSource<bool>"))
.AddVariables(
VariableDeclarator("tsc")
.WithInitializer(
EqualsValueClause(
ImplicitObjectCreationExpression())))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("_synchronizationContext", "Post"))
.AddArgumentListArguments(
Argument(
SimpleLambdaExpression(
Parameter(
Identifier("_")))
.WithAsyncKeyword(Token(SyntaxKind.AsyncKeyword))
.WithBlock(
Block(
TryStatement()
.AddBlockStatements(
ExpressionStatement(
AwaitExpression(
InvocationExpression(
IdentifierName(abstractMethodName))
.AddArgumentListArguments(
inArgs.Select(static (x, i) =>
Argument(
IdentifierName(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}"))).ToArray()))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("tsc", "SetResult"))
.AddArgumentListArguments(
Argument(
LiteralExpression(SyntaxKind.TrueLiteralExpression)))))
.AddCatches(
CatchClause()
.WithDeclaration(CatchDeclaration(ParseTypeName("Exception"))
.WithIdentifier(Identifier("e")))
.AddBlockStatements(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("tsc", "SetException"))
.AddArgumentListArguments(
Argument(IdentifierName("e"))))))))),
Argument(
LiteralExpression(SyntaxKind.NullLiteralExpression)))),
ExpressionStatement(
AwaitExpression(
MakeMemberAccessExpression("tsc", "Task")))),
ElseClause(
Block(
ExpressionStatement(
AwaitExpression(
InvocationExpression(
IdentifierName(abstractMethodName))
.AddArgumentListArguments(
inArgs.Select(static (x, i) =>
Argument(
IdentifierName(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}"))).ToArray())))))));
BlockSyntax replyMethodBlock = Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(
PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, LiteralExpression(SyntaxKind.NullLiteralExpression)))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "Reply"))
.AddArgumentListArguments(
Argument(
InvocationExpression(
MakeMemberAccessExpression("writer", "CreateMessage"))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "Dispose"))));
switchSectionBlock = switchSectionBlock.AddStatements(
IfStatement(
PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, MakeMemberAccessExpression("context", "NoReplyExpected")),
ExpressionStatement(
InvocationExpression(
IdentifierName("Reply")))),
LocalFunctionStatement(
PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Reply"))
.WithBody(replyMethodBlock));
}
}
if (outArgs?.Length > 0)
{
switchSectionBlock = switchSectionBlock.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName(ParseReturnType(outArgs)!))
VariableDeclaration(ParseReturnType(outArgs, AccessMode.Write)!)
.AddVariables(
VariableDeclarator("ret"))),
VariableDeclarator("ret"))));
}
ExpressionSyntax callAbstractMethod = AwaitExpression(
InvocationExpression(
IdentifierName(abstractMethodName))
.AddArgumentListArguments(
inArgs?.Select(static (argument, i) =>
Argument(
IdentifierName(argument.Name is not null
? SanitizeIdentifier(Camelize(argument.Name))
: $"arg{i}")))
.ToArray() ?? []));
switchSectionBlock = switchSectionBlock.AddStatements(
IfStatement(
IsPatternExpression(
IdentifierName("_synchronizationContext"), UnaryPattern(ConstantPattern(LiteralExpression(SyntaxKind.NullLiteralExpression)))),
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName($"TaskCompletionSource<{ParseReturnType(outArgs)!}>"))
VariableDeclaration(
ParseTaskCompletionSourceType(outArgs, AccessMode.Write))
.AddVariables(
VariableDeclarator("tsc")
.WithInitializer(
@ -309,28 +211,27 @@ namespace Tmds.DBus.SourceGenerator
Block(
TryStatement()
.AddBlockStatements(
LocalDeclarationStatement(
outArgs?.Length > 0
? LocalDeclarationStatement(
VariableDeclaration(
ParseTypeName(ParseReturnType(outArgs)!))
ParseReturnType(outArgs, AccessMode.Write)!)
.AddVariables(
VariableDeclarator("ret1")
.WithInitializer(
EqualsValueClause(
AwaitExpression(
InvocationExpression(
IdentifierName(abstractMethodName))
.AddArgumentListArguments(
inArgs?.Select(static (x, i) =>
Argument(
IdentifierName(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}"))).ToArray() ?? Array.Empty<ArgumentSyntax>())))))),
EqualsValueClause(callAbstractMethod))))
: ExpressionStatement(callAbstractMethod),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("tsc", "SetResult"))
.AddArgumentListArguments(
Argument(IdentifierName("ret1")))))
Argument(
outArgs?.Length > 0
? IdentifierName("ret1")
: LiteralExpression(SyntaxKind.TrueLiteralExpression)))))
.AddCatches(
CatchClause()
.WithDeclaration(CatchDeclaration(ParseTypeName("Exception"))
.WithDeclaration(
CatchDeclaration(IdentifierName("Exception"))
.WithIdentifier(Identifier("e")))
.WithBlock(
Block(
@ -342,22 +243,23 @@ namespace Tmds.DBus.SourceGenerator
Argument(
LiteralExpression(SyntaxKind.NullLiteralExpression)))),
ExpressionStatement(
MakeAssignmentExpression(IdentifierName("ret"), AwaitExpression(
MakeMemberAccessExpression("tsc", "Task"))))),
outArgs?.Length > 0
? MakeAssignmentExpression(
IdentifierName("ret"), AwaitExpression(
MakeMemberAccessExpression("tsc", "Task")))
: AwaitExpression(
MakeMemberAccessExpression("tsc", "Task")))),
ElseClause(
Block(
ExpressionStatement(
MakeAssignmentExpression(IdentifierName("ret"), AwaitExpression(
InvocationExpression(
IdentifierName(abstractMethodName))
.AddArgumentListArguments(
inArgs?.Select(static (x, i) =>
Argument(
IdentifierName(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}"))).ToArray() ?? Array.Empty<ArgumentSyntax>()))))))));
outArgs?.Length > 0
? MakeAssignmentExpression(
IdentifierName("ret"), callAbstractMethod)
: callAbstractMethod)))));
BlockSyntax replyMethodBlock = Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageWriter"))
VariableDeclaration(IdentifierName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
@ -365,9 +267,13 @@ namespace Tmds.DBus.SourceGenerator
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(MakeLiteralExpression(ParseSignature(outArgs)!))))))));
Argument(
outArgs?.Length > 0
? MakeLiteralExpression(
ParseSignature(outArgs)!)
: LiteralExpression(SyntaxKind.NullLiteralExpression))))))));
if (outArgs.Length == 1)
if (outArgs?.Length == 1)
{
replyMethodBlock = replyMethodBlock.AddStatements(
ExpressionStatement(
@ -377,7 +283,7 @@ namespace Tmds.DBus.SourceGenerator
Argument(
IdentifierName("ret")))));
}
else
else if (outArgs?.Length > 1)
{
for (int i = 0; i < outArgs.Length; i++)
{
@ -387,7 +293,9 @@ namespace Tmds.DBus.SourceGenerator
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(outArgs[i])))
.AddArgumentListArguments(
Argument(
MakeMemberAccessExpression("ret", outArgs[i].Name is not null ? SanitizeIdentifier(outArgs[i].Name!) : $"Item{i + 1}")))));
MakeMemberAccessExpression("ret", outArgs[i].Name is not null
? SanitizeIdentifier(Pascalize(outArgs[i].Name!))
: $"Item{i + 1}")))));
}
}
@ -404,192 +312,198 @@ namespace Tmds.DBus.SourceGenerator
MakeMemberAccessExpression("writer", "Dispose"))));
switchSectionBlock = switchSectionBlock.AddStatements(
ExpressionStatement(
InvocationExpression(
IdentifierName("Reply"))),
IfStatement(
PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, MakeMemberAccessExpression("context", "NoReplyExpected")),
ExpressionStatement(
InvocationExpression(
IdentifierName("Reply")))),
LocalFunctionStatement(
PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Reply"))
.WithBody(replyMethodBlock));
}
switchSectionBlock = switchSectionBlock.AddStatements(BreakStatement());
switchSections = switchSections.Add(switchSection.AddStatements(switchSectionBlock));
switchSections = switchSections.Add(
switchSection.AddStatements(
switchSectionBlock.AddStatements(
BreakStatement())));
}
sw = sw.AddSections(
SwitchSection()
.AddLabels(
CaseSwitchLabel(MakeLiteralExpression(dBusInterface.Name!)))
.AddStatements(
SwitchStatement(
TupleExpression()
.AddArguments(
Argument(MakeMemberAccessExpression("context", "Request", "MemberAsString")),
Argument(MakeMemberAccessExpression("context", "Request", "SignatureAsString"))))
.WithSections(switchSections),
BreakStatement()));
cl = cl.AddMembers(
MethodDeclaration(
IdentifierName("ValueTask"),
"ReplyInterfaceRequest")
.AddModifiers(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.AsyncKeyword))
.AddParameterListParameters(
Parameter(
Identifier("context"))
.WithType(
IdentifierName("MethodContext")))
.WithBody(
Block(
SwitchStatement(
TupleExpression()
.AddArguments(
Argument(
MakeMemberAccessExpression("context", "Request", "MemberAsString")),
Argument(
MakeMemberAccessExpression("context", "Request", "SignatureAsString"))))
.WithSections(switchSections))));
}
private void AddHandlerProperties(ref ClassDeclarationSyntax cl, ref SwitchStatementSyntax sw, DBusInterface dBusInterface)
private void AddHandlerProperties(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
if (dBusInterface.Properties is null)
return;
dBusInterface.Properties ??= [];
sw = sw.AddSections(
SwitchSection()
.AddLabels(
CaseSwitchLabel(MakeLiteralExpression("org.freedesktop.DBus.Properties")))
.AddStatements(
SwitchStatement(
TupleExpression()
.AddArguments(
Argument(MakeMemberAccessExpression("context", "Request", "MemberAsString")),
Argument(MakeMemberAccessExpression("context", "Request", "SignatureAsString"))))
.AddSections(
SwitchSection()
.AddLabels(
CaseSwitchLabel(
TupleExpression()
.AddArguments(
Argument(MakeLiteralExpression("Get")),
Argument(MakeLiteralExpression("ss")))))
.AddStatements(
Block(
cl = dBusInterface.Properties!.Aggregate(cl, static (current, property) =>
current.AddMembers(
MakeGetSetProperty(
GetDotnetType(property, AccessMode.Write, true),
Pascalize(property.Name!), Token(SyntaxKind.PublicKeyword))));
cl = cl.AddMembers(
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
"ReplyGetProperty")
.AddModifiers(
Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(
Identifier("name"))
.WithType(
PredefinedType(
Token(SyntaxKind.StringKeyword))),
Parameter(
Identifier("context"))
.WithType(
IdentifierName("MethodContext")))
.WithBody(
Block(
SwitchStatement(
IdentifierName("name"))
.WithSections(
List(
dBusInterface.Properties.Select(property =>
SwitchSection()
.AddLabels(
CaseSwitchLabel(
MakeLiteralExpression(property.Name!)))
.AddStatements(
Block(
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression("v"))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteSignature"))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression(property.Type!)))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(property)))
.AddArgumentListArguments(
Argument(
IdentifierName(
Pascalize(property.Name!))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "Reply"))
.AddArgumentListArguments(
Argument(
InvocationExpression(
MakeMemberAccessExpression("writer", "CreateMessage"))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "Dispose"))),
BreakStatement()))))))),
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
"ReplyGetAllProperties")
.AddModifiers(
Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(
Identifier("context"))
.WithType(
IdentifierName("MethodContext")))
.WithBody(
Block(
ExpressionStatement(
InvocationExpression(IdentifierName("Reply"))),
LocalFunctionStatement(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Reply"))
.AddBodyStatements(
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(MakeLiteralExpression("a{sv}"))))))),
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("ArrayStart"))
.AddVariables(
VariableDeclarator("dictStart")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteDictionaryStart")))))))
.AddBodyStatements(
dBusInterface.Properties.SelectMany(property =>
new StatementSyntax[]
{
ExpressionStatement(
InvocationExpression(IdentifierName("Reply"))),
LocalFunctionStatement(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Reply"))
.AddBodyStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "Request",
"GetBodyReader")))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("reader", "ReadString"))),
LocalDeclarationStatement(
VariableDeclaration(PredefinedType(Token(SyntaxKind.StringKeyword)))
.AddVariables(
VariableDeclarator("member")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("reader", "ReadString")))))),
SwitchStatement(IdentifierName("member"))
.WithSections(
List(
dBusInterface.Properties.Select(static dBusProperty =>
SwitchSection()
.AddLabels(
CaseSwitchLabel(
MakeLiteralExpression(dBusProperty.Name!)))
.AddStatements(
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression("v"))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteDBusVariant"))
.AddArgumentListArguments(
Argument(
InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusVariantItem")))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression(ParseSignature(new[] { dBusProperty })!)),
Argument(
MakeGetDBusVariantExpression(dBusProperty,
MakeMemberAccessExpression("BackingProperties", dBusProperty.Name!))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "Reply"))
.AddArgumentListArguments(
Argument(
InvocationExpression(
MakeMemberAccessExpression("writer", "CreateMessage"))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "Dispose"))),
BreakStatement())))))),
BreakStatement())),
SwitchSection()
.AddLabels(
CaseSwitchLabel(
TupleExpression()
.AddArguments(
Argument(MakeLiteralExpression("GetAll")),
Argument(MakeLiteralExpression("s")))))
.AddStatements(
Block(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteDictionaryEntryStart"))),
ExpressionStatement(
InvocationExpression(IdentifierName("Reply"))),
LocalFunctionStatement(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Reply"))
.AddBodyStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageWriter"))
.AddVariables(
VariableDeclarator("writer")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("context", "CreateReplyWriter"))
.AddArgumentListArguments(
Argument(MakeLiteralExpression("a{sv}"))))))),
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("Dictionary<string, DBusVariantItem>"))
.AddVariables(
VariableDeclarator("dict")
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(ParseTypeName("Dictionary<string, DBusVariantItem>"))
.WithInitializer(
InitializerExpression(SyntaxKind.CollectionInitializerExpression)
.WithExpressions(
SeparatedList<ExpressionSyntax>(
dBusInterface.Properties.Select(static dBusProperty =>
InitializerExpression(SyntaxKind.ComplexElementInitializerExpression)
.AddExpressions(
MakeLiteralExpression(dBusProperty.Name!), InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusVariantItem")))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression(ParseSignature(new [] { dBusProperty })!)),
Argument(
MakeGetDBusVariantExpression(dBusProperty, MakeMemberAccessExpression("BackingProperties", dBusProperty.Name!))))))))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(new DBusValue { Type = "a{sv}" })))
.AddArgumentListArguments(
Argument(IdentifierName("dict")))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "Reply"))
.AddArgumentListArguments(
Argument(
InvocationExpression(MakeMemberAccessExpression("writer", "CreateMessage")))))),
BreakStatement()))),
BreakStatement()));
AddPropertiesClass(ref cl, dBusInterface);
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteString"))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression(property.Name!)))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteSignature"))
.AddArgumentListArguments(
Argument(
MakeLiteralExpression(property.Type!)))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(property)))
.AddArgumentListArguments(
Argument(
IdentifierName(
Pascalize(property.Name!)))))
}).ToArray())
.AddBodyStatements(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteDictionaryEnd"))
.AddArgumentListArguments(
Argument(
IdentifierName("dictStart")))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "Reply"))
.AddArgumentListArguments(
Argument(
InvocationExpression(MakeMemberAccessExpression("writer", "CreateMessage")))))))));
}
private void AddHandlerIntrospect(ref ClassDeclarationSyntax cl, ref SwitchStatementSyntax sw, DBusInterface dBusInterface)
private void AddHandlerIntrospect(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
XmlSerializer xmlSerializer = new(typeof(DBusInterface));
using StringWriter stringWriter = new();
@ -598,75 +512,52 @@ namespace Tmds.DBus.SourceGenerator
string introspect = stringWriter.ToString();
cl = cl.AddMembers(
FieldDeclaration(
VariableDeclaration(ParseTypeName("ReadOnlyMemory<byte>"))
.AddVariables(
VariableDeclarator("_introspectXml")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, LiteralExpression(SyntaxKind.Utf8StringLiteralExpression, Utf8Literal(introspect)), IdentifierName("ToArray")))))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword)));
sw = sw.AddSections(
SwitchSection()
.AddLabels(
CaseSwitchLabel(MakeLiteralExpression("org.freedesktop.DBus.Introspectable")))
.AddStatements(
SwitchStatement(
TupleExpression()
.AddArguments(
Argument(MakeMemberAccessExpression("context", "Request", "MemberAsString")),
Argument(MakeMemberAccessExpression("context", "Request", "SignatureAsString"))))
.AddSections(
SwitchSection()
.AddLabels(
CasePatternSwitchLabel(
RecursivePattern()
.WithPositionalPatternClause(
PositionalPatternClause()
.AddSubpatterns(
Subpattern(
ConstantPattern(MakeLiteralExpression("Introspect"))),
Subpattern(
BinaryPattern(SyntaxKind.OrPattern, ConstantPattern(MakeLiteralExpression(string.Empty)), ConstantPattern(LiteralExpression(SyntaxKind.NullLiteralExpression)))))),
Token(SyntaxKind.ColonToken)))
.AddStatements(
Block(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("context", "ReplyIntrospectXml"))
.AddArgumentListArguments(
Argument(
ImplicitArrayCreationExpression(
InitializerExpression(SyntaxKind.ArrayInitializerExpression)
.AddExpressions(IdentifierName("_introspectXml")))))),
BreakStatement()))),
BreakStatement()));
MakeGetOnlyProperty(
GenericName("ReadOnlyMemory")
.AddTypeArgumentListArguments(
PredefinedType(
Token(SyntaxKind.ByteKeyword))),
"IntrospectXml",
Token(SyntaxKind.PublicKeyword))
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, LiteralExpression(SyntaxKind.Utf8StringLiteralExpression, Utf8Literal(introspect)), IdentifierName("ToArray")))))
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken)));
}
private void AddHandlerSignals(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
if (dBusInterface.Signals is null)
return;
foreach (DBusSignal dBusSignal in dBusInterface.Signals)
{
MethodDeclarationSyntax method = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), $"Emit{Pascalize(dBusSignal.Name!)}")
.AddModifiers(Token(SyntaxKind.ProtectedKeyword));
if (dBusSignal.Arguments?.Length > 0)
foreach (DBusSignal signal in dBusInterface.Signals)
{
MethodDeclarationSyntax method = MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
$"Emit{Pascalize(signal.Name!)}")
.AddModifiers(
Token(SyntaxKind.ProtectedKeyword));
if (signal.Arguments?.Length > 0)
{
method = method.WithParameterList(
ParameterList(
SeparatedList(
dBusSignal.Arguments.Select(
static (x, i) => Parameter(Identifier(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}")).WithType(ParseTypeName(x.DotNetType))))));
signal.Arguments.Select(
static (argument, i) => Parameter(
Identifier(argument.Name is not null ? SanitizeIdentifier(Camelize(argument.Name)) : $"arg{i}"))
.WithType(
GetDotnetType(argument, AccessMode.Write, true))))));
}
BlockSyntax body = Block();
body = body.AddStatements(
LocalDeclarationStatement(VariableDeclaration(ParseTypeName("MessageWriter"),
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("MessageWriter"),
SingletonSeparatedList(
VariableDeclarator("writer")
.WithInitializer(EqualsValueClause(
@ -675,15 +566,21 @@ namespace Tmds.DBus.SourceGenerator
ArgumentListSyntax args = ArgumentList()
.AddArguments(
Argument(LiteralExpression(SyntaxKind.NullLiteralExpression)),
Argument(IdentifierName("Path")),
Argument(MakeLiteralExpression(dBusInterface.Name!)),
Argument(MakeLiteralExpression(dBusSignal.Name!)));
if (dBusSignal.Arguments?.Length > 0)
Argument(
LiteralExpression(SyntaxKind.NullLiteralExpression)),
Argument(
MakeMemberAccessExpression("PathHandler", "Path")),
Argument(
MakeLiteralExpression(dBusInterface.Name!)),
Argument(
MakeLiteralExpression(signal.Name!)));
if (signal.Arguments?.Length > 0)
{
args = args.AddArguments(
Argument(MakeLiteralExpression(ParseSignature(dBusSignal.Arguments)!)));
Argument(
MakeLiteralExpression(
ParseSignature(signal.Arguments)!)));
}
body = body.AddStatements(
@ -692,17 +589,19 @@ namespace Tmds.DBus.SourceGenerator
MakeMemberAccessExpression("writer", "WriteSignalHeader"))
.WithArgumentList(args)));
if (dBusSignal.Arguments?.Length > 0)
if (signal.Arguments?.Length > 0)
{
for (int i = 0; i < dBusSignal.Arguments.Length; i++)
for (int i = 0; i < signal.Arguments.Length; i++)
{
body = body.AddStatements(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusSignal.Arguments[i])))
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(signal.Arguments[i])))
.AddArgumentListArguments(
Argument(
IdentifierName(dBusSignal.Arguments[i].Name ?? $"arg{i}")))));
IdentifierName(signal.Arguments[i].Name is not null
? Camelize(signal.Arguments[i].Name!)
: $"arg{i}")))));
}
}

197
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Parsing.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@ -12,6 +13,21 @@ namespace Tmds.DBus.SourceGenerator
{
public partial class DBusSourceGenerator
{
private static readonly DBusValue _byteValue = new() { Type = "y" };
private static readonly DBusValue _boolValue = new() { Type = "b" };
private static readonly DBusValue _int16Value = new() { Type = "n" };
private static readonly DBusValue _uInt16Value = new() { Type = "q" };
private static readonly DBusValue _int32Value = new() { Type = "i" };
private static readonly DBusValue _uInt32Value = new() { Type = "u" };
private static readonly DBusValue _int64Value = new() { Type = "x" };
private static readonly DBusValue _uInt64Value = new() { Type = "t" };
private static readonly DBusValue _doubleValue = new() { Type = "d" };
private static readonly DBusValue _stringValue = new() { Type = "s" };
private static readonly DBusValue _objectPathValue = new() { Type = "o" };
private static readonly DBusValue _signatureValue = new() { Type = "g" };
private static readonly DBusValue _variantValue = new() { Type = "v" };
private static readonly DBusValue _unixFdValue = new() { Type = "h" };
private static string Pascalize(string name, bool camel = false)
{
bool upperizeNext = !camel;
@ -38,71 +54,178 @@ namespace Tmds.DBus.SourceGenerator
return sb.ToString();
}
private static string Camelize(string name)
{
StringBuilder sb = new(Pascalize(name));
sb[0] = char.ToLowerInvariant(sb[0]);
return sb.ToString();
}
private static string? ParseSignature(IReadOnlyList<DBusValue>? dBusValues)
{
if (dBusValues is null || dBusValues.Count == 0)
return null;
StringBuilder sb = new();
foreach (DBusValue dBusValue in dBusValues.Where(static argument => argument.Type is not null))
sb.Append(dBusValue.Type);
return sb.ToString();
}
private static string? ParseReturnType(IReadOnlyList<DBusValue>? dBusValues) => dBusValues?.Count switch
private static TypeSyntax? ParseReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
{
0 or null => null,
1 => dBusValues[0].DotNetType,
_ => TupleOf(dBusValues.Select(static (x, i) => $"{x.DotNetType} {(x.Name is not null ? SanitizeIdentifier(x.Name) : $"Item{i + 1}")}"))
1 => GetDotnetType(dBusValues[0], accessMode),
_ => TupleType()
.AddElements(
dBusValues.Select((dBusValue, i) => TupleElement(GetDotnetType(dBusValue, accessMode))
.WithIdentifier(
Identifier(dBusValue.Name is not null ? SanitizeIdentifier(Pascalize(dBusValue.Name)) : $"Item{i + 1}")))
.ToArray())
};
private static string ParseTaskReturnType(DBusValue dBusValue) => ParseTaskReturnType(new[] { dBusValue });
private static TypeSyntax ParseTaskReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
{
0 or null => IdentifierName("Task"),
_ => GenericName("Task")
.AddTypeArgumentListArguments(
ParseReturnType(dBusValues, accessMode)!)
};
private static string ParseTaskReturnType(IReadOnlyList<DBusValue>? dBusValues) => dBusValues?.Count switch
private static TypeSyntax ParseValueTaskReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
{
0 or null => "Task",
1 => $"Task<{dBusValues[0].DotNetType}>",
_ => $"Task<{TupleOf(dBusValues.Select(static (x, i) => $"{x.DotNetType} {(x.Name is not null ? SanitizeIdentifier(x.Name) : $"Item{i + 1}")}"))}>"
0 or null => IdentifierName("ValueTask"),
_ => GenericName("ValueTask")
.AddTypeArgumentListArguments(
ParseReturnType(dBusValues, accessMode)!)
};
private static ParameterListSyntax ParseParameterList(IEnumerable<DBusValue> inArgs) => ParameterList(
private static TypeSyntax ParseTaskCompletionSourceType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
{
0 or null => GenericName("TaskCompletionSource")
.AddTypeArgumentListArguments(
PredefinedType(
Token(SyntaxKind.BoolKeyword))),
_ => GenericName("TaskCompletionSource")
.AddTypeArgumentListArguments(
ParseReturnType(dBusValues, accessMode)!)
};
private static ParameterListSyntax ParseParameterList(IEnumerable<DBusValue> inArgs, AccessMode accessMode) => ParameterList(
SeparatedList(
inArgs.Select(static (x, i) =>
Parameter(Identifier(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}")).WithType(ParseTypeName(x.DotNetType)))));
inArgs.Select((x, i) =>
Parameter(Identifier(x.Name is not null ? SanitizeIdentifier(Camelize(x.Name)) : $"arg{i}")).WithType(GetDotnetType(x, accessMode)))));
private static string SanitizeSignature(string signature) =>
signature.Replace('{', 'e').Replace("}", null).Replace('(', 'r').Replace(')', 'z');
signature.Replace('{', 'e')
.Replace("}", null)
.Replace('(', 'r')
.Replace(')', 'z');
private static string SanitizeIdentifier(string identifier) => $"@{identifier}";
private static string SanitizeIdentifier(string identifier)
{
bool isAnyKeyword = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None || SyntaxFacts.GetContextualKeywordKind(identifier) != SyntaxKind.None;
return isAnyKeyword ? $"@{identifier}" : identifier;
}
internal static (string DotnetType, string[] InnerDotNetTypes, DBusValue DBusValue, DBusValue[] InnerDBusTypes, DBusType DBusType) ParseDBusValue(string signature) =>
SignatureReader.Transform<(string, string[], DBusValue, DBusValue[], DBusType)>(Encoding.ASCII.GetBytes(signature), MapDBusToDotNet);
internal static (DBusValue DBusValue, DBusValue[] InnerDBusTypes, DBusType DBusType) ParseDBusValue(string signature) =>
SignatureReader.Transform<(DBusValue, DBusValue[], DBusType)>(Encoding.ASCII.GetBytes(signature), MapDBusToDotNet);
private static (string, string[], DBusValue, DBusValue[], DBusType) MapDBusToDotNet(DBusType dBusType, (string, string[], DBusValue, DBusValue[], DBusType)[] inner)
private static (DBusValue, DBusValue[], DBusType) MapDBusToDotNet(DBusType dBusType, (DBusValue, DBusValue[], DBusType)[] inner)
{
string[] innerDotNetTypes = inner.Select(static x => x.Item1).ToArray();
DBusValue[] innerDBusTypes = inner.Select(static x => x.Item3).ToArray();
DBusValue[] innerDBusTypes = inner.Select(static x => x.Item1).ToArray();
return dBusType switch
{
DBusType.Byte => ("byte", innerDotNetTypes, new DBusValue { Type = "y" }, innerDBusTypes, dBusType),
DBusType.Bool => ("bool", innerDotNetTypes, new DBusValue { Type = "b" }, innerDBusTypes, dBusType),
DBusType.Int16 => ("short", innerDotNetTypes, new DBusValue { Type = "n" }, innerDBusTypes, dBusType),
DBusType.UInt16 => ("ushort", innerDotNetTypes, new DBusValue { Type = "q" }, innerDBusTypes, dBusType),
DBusType.Int32 => ("int", innerDotNetTypes, new DBusValue { Type = "i" }, innerDBusTypes, dBusType),
DBusType.UInt32 => ("uint", innerDotNetTypes, new DBusValue { Type = "u" }, innerDBusTypes, dBusType),
DBusType.Int64 => ("long", innerDotNetTypes, new DBusValue { Type = "x" }, innerDBusTypes, dBusType),
DBusType.UInt64 => ("ulong", innerDotNetTypes, new DBusValue { Type = "t" }, innerDBusTypes, dBusType),
DBusType.Double => ("double", innerDotNetTypes, new DBusValue { Type = "d" }, innerDBusTypes, dBusType),
DBusType.String => ("string", innerDotNetTypes, new DBusValue { Type = "s" }, innerDBusTypes, dBusType),
DBusType.ObjectPath => ("ObjectPath", innerDotNetTypes, new DBusValue { Type = "o" }, innerDBusTypes, dBusType),
DBusType.Signature => ("Signature", innerDotNetTypes, new DBusValue { Type = "g" }, innerDBusTypes, dBusType),
DBusType.Variant => ("DBusVariantItem", innerDotNetTypes, new DBusValue { Type = "v" }, innerDBusTypes, dBusType),
DBusType.UnixFd => ("SafeHandle", innerDotNetTypes, new DBusValue { Type = "h" }, innerDBusTypes, dBusType),
DBusType.Array => ($"{innerDotNetTypes[0]}[]", innerDotNetTypes, new DBusValue { Type = $"a{ParseSignature(innerDBusTypes)}" }, innerDBusTypes, dBusType),
DBusType.DictEntry => ($"Dictionary<{innerDotNetTypes[0]}, {innerDotNetTypes[1]}>", innerDotNetTypes, new DBusValue { Type = $"a{{{ParseSignature(innerDBusTypes)}}}" }, innerDBusTypes, dBusType),
DBusType.Struct when innerDotNetTypes.Length == 1 => ($"ValueTuple<{innerDotNetTypes[0]}>", innerDotNetTypes, new DBusValue { Type = $"({ParseSignature(innerDBusTypes)}" }, innerDBusTypes, dBusType),
DBusType.Struct => ($"{TupleOf(innerDotNetTypes)}", innerDotNetTypes, new DBusValue { Type = $"({ParseSignature(innerDBusTypes)})" }, innerDBusTypes, dBusType),
_ => throw new ArgumentOutOfRangeException(nameof(dBusType), dBusType, null)
DBusType.Byte => (_byteValue, innerDBusTypes, dBusType),
DBusType.Bool => (_boolValue, innerDBusTypes, dBusType),
DBusType.Int16 => (_int16Value, innerDBusTypes, dBusType),
DBusType.UInt16 => (_uInt16Value, innerDBusTypes, dBusType),
DBusType.Int32 => (_int32Value, innerDBusTypes, dBusType),
DBusType.UInt32 => (_uInt32Value, innerDBusTypes, dBusType),
DBusType.Int64 => (_int64Value, innerDBusTypes, dBusType),
DBusType.UInt64 => (_uInt64Value, innerDBusTypes, dBusType),
DBusType.Double => (_doubleValue, innerDBusTypes, dBusType),
DBusType.String => (_stringValue, innerDBusTypes, dBusType),
DBusType.ObjectPath => (_objectPathValue, innerDBusTypes, dBusType),
DBusType.Signature => (_signatureValue, innerDBusTypes, dBusType),
DBusType.Variant => (_variantValue, innerDBusTypes, dBusType),
DBusType.UnixFd => (_unixFdValue, innerDBusTypes, dBusType),
DBusType.Array => (new DBusValue { Type = $"a{ParseSignature(innerDBusTypes)}"}, innerDBusTypes, dBusType),
DBusType.DictEntry => (new DBusValue { Type = $"a{{{ParseSignature(innerDBusTypes)}}}"}, innerDBusTypes, dBusType),
DBusType.Struct => (new DBusValue { Type = $"({ParseSignature(innerDBusTypes)})"}, innerDBusTypes, dBusType),
_ => throw new ArgumentOutOfRangeException(nameof(dBusType), dBusType, $"Cannot parse DBusType with value {dBusType}")
};
}
private static TypeSyntax GetDotnetType(DBusValue dBusValue, AccessMode accessMode, bool nullable = false)
{
switch (dBusValue.DBusType)
{
case DBusType.Byte:
return PredefinedType(Token(SyntaxKind.ByteKeyword));
case DBusType.Bool:
return PredefinedType(Token(SyntaxKind.BoolKeyword));
case DBusType.Int16:
return PredefinedType(Token(SyntaxKind.ShortKeyword));
case DBusType.UInt16:
return PredefinedType(Token(SyntaxKind.UShortKeyword));
case DBusType.Int32:
return PredefinedType(Token(SyntaxKind.IntKeyword));
case DBusType.UInt32:
return PredefinedType(Token(SyntaxKind.UIntKeyword));
case DBusType.Int64:
return PredefinedType(Token(SyntaxKind.LongKeyword));
case DBusType.UInt64:
return PredefinedType(Token(SyntaxKind.ULongKeyword));
case DBusType.Double:
return PredefinedType(Token(SyntaxKind.DoubleKeyword));
case DBusType.String:
TypeSyntax str = PredefinedType(Token(SyntaxKind.StringKeyword));
if (nullable)
str = NullableType(str);
return str;
case DBusType.ObjectPath:
return IdentifierName("ObjectPath");
case DBusType.Signature:
return IdentifierName("Signature");
case DBusType.Variant when accessMode == AccessMode.Read:
return IdentifierName("VariantValue");
case DBusType.Variant when accessMode == AccessMode.Write:
return IdentifierName("Variant");
case DBusType.UnixFd:
return IdentifierName("SafeFileHandle");
case DBusType.Array:
TypeSyntax arr = ArrayType(
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode, nullable))
.AddRankSpecifiers(ArrayRankSpecifier()
.AddSizes(OmittedArraySizeExpression()));
if (nullable)
arr = NullableType(arr);
return arr;
case DBusType.DictEntry:
TypeSyntax dict = GenericName("Dictionary")
.AddTypeArgumentListArguments(
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode),
GetDotnetType(dBusValue.InnerDBusTypes![1], accessMode, nullable));
if (nullable)
dict = NullableType(dict);
return dict;
case DBusType.Struct when dBusValue.InnerDBusTypes!.Length == 1:
return GenericName("ValueTuple")
.AddTypeArgumentListArguments(
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode, nullable));
case DBusType.Struct:
return TupleType()
.AddElements(
dBusValue.InnerDBusTypes!.Select(
dbusType => TupleElement(
GetDotnetType(dbusType, accessMode, nullable)))
.ToArray());
default:
throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType,
$"Cannot parse DBusType with value {dBusValue.DBusType}");
}
}
}
}

332
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Proxy.cs

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
@ -19,16 +18,21 @@ namespace Tmds.DBus.SourceGenerator
.AddModifiers(Token(SyntaxKind.InternalKeyword));
FieldDeclarationSyntax interfaceConst = MakePrivateStringConst("Interface", dBusInterface.Name!, PredefinedType(Token(SyntaxKind.StringKeyword)));
FieldDeclarationSyntax connectionField = MakePrivateReadOnlyField("_connection", ParseTypeName("Connection"));
FieldDeclarationSyntax connectionField = MakePrivateReadOnlyField("_connection", IdentifierName("Connection"));
FieldDeclarationSyntax destinationField = MakePrivateReadOnlyField("_destination", PredefinedType(Token(SyntaxKind.StringKeyword)));
FieldDeclarationSyntax pathField = MakePrivateReadOnlyField("_path", PredefinedType(Token(SyntaxKind.StringKeyword)));
ConstructorDeclarationSyntax ctor = ConstructorDeclaration(identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("connection")).WithType(ParseTypeName("Connection")),
Parameter(Identifier("destination")).WithType(PredefinedType(Token(SyntaxKind.StringKeyword))),
Parameter(Identifier("path")).WithType(PredefinedType(Token(SyntaxKind.StringKeyword))))
Parameter(Identifier("connection"))
.WithType(IdentifierName("Connection")),
Parameter(Identifier("destination"))
.WithType(
PredefinedType(Token(SyntaxKind.StringKeyword))),
Parameter(Identifier("path"))
.WithType(
PredefinedType(Token(SyntaxKind.StringKeyword))))
.WithBody(
Block(
MakeAssignmentExpressionStatement("_connection", "connection"),
@ -36,9 +40,10 @@ namespace Tmds.DBus.SourceGenerator
MakeAssignmentExpressionStatement("_path", "path")));
cl = cl.AddMembers(interfaceConst, connectionField, destinationField, pathField, ctor);
AddProperties(ref cl, dBusInterface);
AddProxyMethods(ref cl, dBusInterface);
AddProxySignals(ref cl, dBusInterface);
AddProperties(ref cl, dBusInterface);
return cl;
}
@ -47,13 +52,12 @@ namespace Tmds.DBus.SourceGenerator
{
if (dBusInterface.Methods is null)
return;
foreach (DBusMethod dBusMethod in dBusInterface.Methods)
{
DBusArgument[]? inArgs = dBusMethod.Arguments?.Where(static m => m.Direction is null or "in").ToArray();
DBusArgument[]? outArgs = dBusMethod.Arguments?.Where(static m => m.Direction == "out").ToArray();
string returnType = ParseTaskReturnType(outArgs);
ArgumentListSyntax args = ArgumentList(
SingletonSeparatedList(
Argument(
@ -67,21 +71,21 @@ namespace Tmds.DBus.SourceGenerator
MakeMemberAccessExpression("ReaderExtensions", GetOrAddReadMessageMethod(outArgs))));
}
StatementSyntax[] statements = inArgs?.Select((x, i) => ExpressionStatement(
StatementSyntax[] statements = inArgs?.Select((arg, i) => ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(x)))
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(arg)))
.AddArgumentListArguments(
Argument(IdentifierName(x.Name is not null ? SanitizeIdentifier(x.Name) : $"arg{i}")))))
Argument(IdentifierName(arg.Name is not null ? SanitizeIdentifier(Camelize(arg.Name)) : $"arg{i}")))))
.Cast<StatementSyntax>()
.ToArray() ?? Array.Empty<StatementSyntax>();
.ToArray() ?? [];
BlockSyntax createMessageBody = MakeCreateMessageBody(IdentifierName("Interface"), dBusMethod.Name!, ParseSignature(inArgs), statements);
MethodDeclarationSyntax proxyMethod = MethodDeclaration(ParseTypeName(returnType), $"{Pascalize(dBusMethod.Name!)}Async")
MethodDeclarationSyntax proxyMethod = MethodDeclaration(ParseTaskReturnType(outArgs, AccessMode.Read), $"{Pascalize(dBusMethod.Name!)}Async")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
if (inArgs is not null)
proxyMethod = proxyMethod.WithParameterList(ParseParameterList(inArgs));
proxyMethod = proxyMethod.WithParameterList(ParseParameterList(inArgs, AccessMode.Write));
cl = cl.AddMembers(proxyMethod.WithBody(MakeCallMethodReturnBody(args, createMessageBody)));
}
@ -89,48 +93,69 @@ namespace Tmds.DBus.SourceGenerator
private void AddProxySignals(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
if (dBusInterface.Signals is null) return;
if (dBusInterface.Signals is null)
return;
foreach (DBusSignal dBusSignal in dBusInterface.Signals)
{
DBusArgument[]? outArgs = dBusSignal.Arguments?.Where(static x => x.Direction is null or "out").ToArray();
string? returnType = ParseReturnType(outArgs);
TypeSyntax? returnType = ParseReturnType(outArgs, AccessMode.Read);
ParameterListSyntax parameters = ParameterList();
parameters = returnType is not null
? parameters.AddParameters(
Parameter(Identifier("handler"))
.WithType(ParseTypeName($"Action<Exception?, {returnType}>")))
.WithType(
GenericName("Action")
.AddTypeArgumentListArguments(
NullableType(
IdentifierName("Exception")),
returnType)))
: parameters.AddParameters(
Parameter(Identifier("handler"))
.WithType(ParseTypeName("Action<Exception?>")));
.WithType(
GenericName("Action")
.AddTypeArgumentListArguments(
NullableType(
IdentifierName("Exception")))));
parameters = parameters.AddParameters(
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.TrueLiteralExpression))));
.WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.TrueLiteralExpression))),
Parameter(Identifier("flags"))
.WithType(IdentifierName("ObserverFlags"))
.WithDefault(EqualsValueClause(MakeMemberAccessExpression("ObserverFlags", "None"))));
ArgumentListSyntax arguments = ArgumentList()
.AddArguments(Argument(IdentifierName("_connection")),
.AddArguments(
Argument(IdentifierName("_connection")),
Argument(IdentifierName("rule")));
if (outArgs is not null)
{
arguments = arguments.AddArguments(
Argument(MakeMemberAccessExpression("ReaderExtensions", GetOrAddReadMessageMethod(outArgs))));
Argument(
MakeMemberAccessExpression("ReaderExtensions", GetOrAddReadMessageMethod(outArgs))));
}
arguments = arguments.AddArguments(
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext")));
MethodDeclarationSyntax watchSignalMethod = MethodDeclaration(ParseTypeName("ValueTask<IDisposable>"), $"Watch{Pascalize(dBusSignal.Name!)}Async")
Argument(IdentifierName("emitOnCapturedContext")),
Argument(IdentifierName("flags")));
MethodDeclarationSyntax watchSignalMethod = MethodDeclaration(
GenericName("ValueTask")
.AddTypeArgumentListArguments(
IdentifierName("IDisposable")),
$"Watch{Pascalize(dBusSignal.Name!)}Async")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithParameterList(parameters)
.WithBody(
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MatchRule"))
VariableDeclaration(IdentifierName("MatchRule"))
.AddVariables(
VariableDeclarator("rule")
.WithInitializer(
@ -145,7 +170,7 @@ namespace Tmds.DBus.SourceGenerator
}
private static ObjectCreationExpressionSyntax MakeMatchRule(DBusSignal dBusSignal) =>
ObjectCreationExpression(ParseTypeName("MatchRule"))
ObjectCreationExpression(IdentifierName("MatchRule"))
.WithInitializer(
InitializerExpression(SyntaxKind.ObjectInitializerExpression)
.AddExpressions(
@ -156,78 +181,106 @@ namespace Tmds.DBus.SourceGenerator
MakeAssignmentExpression(IdentifierName("Interface"), IdentifierName("Interface"))));
private void AddWatchPropertiesChanged(ref ClassDeclarationSyntax cl) =>
cl = cl.AddMembers(MethodDeclaration(ParseTypeName("ValueTask<IDisposable>"), "WatchPropertiesChangedAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("handler"))
.WithType(ParseTypeName("Action<Exception?, PropertyChanges<Properties>>")),
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(
EqualsValueClause(
LiteralExpression(SyntaxKind.TrueLiteralExpression))))
.WithBody(
Block(
ReturnStatement(
InvocationExpression(
MakeMemberAccessExpression("SignalHelper", "WatchPropertiesChangedAsync"))
.AddArgumentListArguments(
Argument(IdentifierName("_connection")),
Argument(IdentifierName("_destination")),
Argument(IdentifierName("_path")),
Argument(IdentifierName("Interface")),
Argument(IdentifierName("ReadMessage")),
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext")))),
LocalFunctionStatement(ParseTypeName("PropertyChanges<Properties>"), "ReadMessage")
.AddModifiers(Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("message"))
.WithType(ParseTypeName("Message")),
Parameter(Identifier("_"))
.WithType(
NullableType(
PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithBody(
Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("message", "GetBodyReader")))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("reader", "ReadString"))),
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("List<string>"))
.AddVariables(
VariableDeclarator("changed")
.WithInitializer(
EqualsValueClause(
ImplicitObjectCreationExpression())))),
ReturnStatement(
InvocationExpression(
ObjectCreationExpression(ParseTypeName("PropertyChanges<Properties>")))
.AddArgumentListArguments(
Argument(InvocationExpression(
IdentifierName("ReadProperties"))
.AddArgumentListArguments(
Argument(IdentifierName("reader"))
.WithRefKindKeyword(Token(SyntaxKind.RefKeyword)),
Argument(IdentifierName("changed")))),
Argument(
InvocationExpression(
MakeMemberAccessExpression("changed", "ToArray"))),
Argument(
InvocationExpression(
MakeMemberAccessExpression("reader", GetOrAddReadMethod(new DBusValue { Type = "as" })))))))))));
cl = cl.AddMembers(
MethodDeclaration(
GenericName("ValueTask")
.AddTypeArgumentListArguments(
IdentifierName("IDisposable")),
"WatchPropertiesChangedAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("handler"))
.WithType(GenericName("Action")
.AddTypeArgumentListArguments(
NullableType(
IdentifierName("Exception")),
GenericName("PropertyChanges")
.AddTypeArgumentListArguments(
IdentifierName("Properties")))),
Parameter(Identifier("emitOnCapturedContext"))
.WithType(PredefinedType(Token(SyntaxKind.BoolKeyword)))
.WithDefault(
EqualsValueClause(
LiteralExpression(SyntaxKind.TrueLiteralExpression))),
Parameter(Identifier("flags"))
.WithType(IdentifierName("ObserverFlags"))
.WithDefault(
EqualsValueClause(
MakeMemberAccessExpression("ObserverFlags", "None"))))
.WithBody(
Block(
ReturnStatement(
InvocationExpression(
MakeMemberAccessExpression("SignalHelper", "WatchPropertiesChangedAsync"))
.AddArgumentListArguments(
Argument(IdentifierName("_connection")),
Argument(IdentifierName("_destination")),
Argument(IdentifierName("_path")),
Argument(IdentifierName("Interface")),
Argument(IdentifierName("ReadMessage")),
Argument(IdentifierName("handler")),
Argument(IdentifierName("emitOnCapturedContext")),
Argument(IdentifierName("flags")))),
LocalFunctionStatement(
GenericName("PropertyChanges")
.AddTypeArgumentListArguments(
IdentifierName("Properties")),
"ReadMessage")
.AddModifiers(Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("message"))
.WithType(IdentifierName("Message")),
Parameter(Identifier("_"))
.WithType(
NullableType(
PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithBody(
Block(
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("message", "GetBodyReader")))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("reader", "ReadString"))),
LocalDeclarationStatement(
VariableDeclaration(
GenericName("List")
.AddTypeArgumentListArguments(
PredefinedType(Token(SyntaxKind.StringKeyword))))
.AddVariables(
VariableDeclarator("changed")
.WithInitializer(
EqualsValueClause(
ImplicitObjectCreationExpression())))),
ReturnStatement(
InvocationExpression(
ObjectCreationExpression(GenericName("PropertyChanges")
.AddTypeArgumentListArguments(
IdentifierName("Properties"))))
.AddArgumentListArguments(
Argument(InvocationExpression(
IdentifierName("ReadProperties"))
.AddArgumentListArguments(
Argument(IdentifierName("reader"))
.WithRefKindKeyword(Token(SyntaxKind.RefKeyword)),
Argument(IdentifierName("changed")))),
Argument(
InvocationExpression(
MakeMemberAccessExpression("changed", "ToArray"))),
Argument(
InvocationExpression(
MakeMemberAccessExpression("reader",
GetOrAddReadMethod(new DBusValue { Type = "as" })))))))))));
private void AddProperties(ref ClassDeclarationSyntax cl, DBusInterface dBusInterface)
{
if (dBusInterface.Properties is null || dBusInterface.Properties.Length == 0) return;
if (dBusInterface.Properties is null || dBusInterface.Properties.Length == 0)
return;
cl = dBusInterface.Properties!.Aggregate(cl, (current, dBusProperty) => dBusProperty.Access switch
{
@ -263,7 +316,7 @@ namespace Tmds.DBus.SourceGenerator
Argument(
MakeMemberAccessExpression("ReaderExtensions", GetOrAddReadMessageMethod(dBusProperty, true))));
return MethodDeclaration(ParseTypeName(ParseTaskReturnType(dBusProperty)), $"Get{Pascalize(dBusProperty.Name!)}PropertyAsync")
return MethodDeclaration(ParseTaskReturnType([dBusProperty], AccessMode.Read), $"Get{Pascalize(dBusProperty.Name!)}PropertyAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithBody(
MakeCallMethodReturnBody(args, createMessageBody));
@ -279,7 +332,7 @@ namespace Tmds.DBus.SourceGenerator
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteString"))
.AddArgumentListArguments(Argument(MakeLiteralExpression(dBusProperty.Name!)))),
.AddArgumentListArguments(Argument(MakeLiteralExpression(Pascalize(dBusProperty.Name!))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteVariant"))
@ -291,10 +344,13 @@ namespace Tmds.DBus.SourceGenerator
InvocationExpression(
IdentifierName("CreateMessage")))));
return MethodDeclaration(ParseTypeName("Task"), $"Set{dBusProperty.Name}PropertyAsync")
return MethodDeclaration(
IdentifierName("Task"),
$"Set{Pascalize(dBusProperty.Name!)}PropertyAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(
Parameter(Identifier("value")).WithType(ParseTypeName(dBusProperty.DotNetType)))
Parameter(Identifier("value"))
.WithType(GetDotnetType(dBusProperty, AccessMode.Write)))
.WithBody(
MakeCallMethodReturnBody(args, createMessageBody));
}
@ -309,26 +365,34 @@ namespace Tmds.DBus.SourceGenerator
ParenthesizedLambdaExpressionSyntax messageValueReaderLambda = ParenthesizedLambdaExpression()
.AddParameterListParameters(
Parameter(Identifier("message")).WithType(ParseTypeName("Message")),
Parameter(Identifier("state")).WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
Parameter(Identifier("message"))
.WithType(IdentifierName("Message")),
Parameter(Identifier("state"))
.WithType(
NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithBody(
Block(
LocalDeclarationStatement(VariableDeclaration(ParseTypeName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("message", "GetBodyReader")))))),
LocalDeclarationStatement(
VariableDeclaration(IdentifierName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
MakeMemberAccessExpression("message", "GetBodyReader")))))),
ReturnStatement(
InvocationExpression(
IdentifierName("ReadProperties"))
IdentifierName("ReadProperties"))
.AddArgumentListArguments(
Argument(IdentifierName("reader"))
.WithRefKindKeyword(Token(SyntaxKind.RefKeyword))))));
cl = cl.AddMembers(
MethodDeclaration(ParseTypeName("Task<Properties>"), "GetAllPropertiesAsync")
MethodDeclaration(
GenericName("Task")
.AddTypeArgumentListArguments(
IdentifierName("Properties")),
"GetAllPropertiesAsync")
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithBody(
Block(
@ -336,9 +400,10 @@ namespace Tmds.DBus.SourceGenerator
InvocationExpression(
MakeMemberAccessExpression("_connection", "CallMethodAsync"))
.AddArgumentListArguments(
Argument(InvocationExpression(IdentifierName("CreateGetAllMessage"))),
Argument(
InvocationExpression(IdentifierName("CreateGetAllMessage"))),
Argument(messageValueReaderLambda))),
LocalFunctionStatement(ParseTypeName("MessageBuffer"), "CreateGetAllMessage")
LocalFunctionStatement(IdentifierName("MessageBuffer"), "CreateGetAllMessage")
.WithBody(createGetAllMessageBody))));
}
@ -347,36 +412,41 @@ namespace Tmds.DBus.SourceGenerator
ClassDeclarationSyntax propertiesClass = ClassDeclaration("Properties")
.AddModifiers(Token(SyntaxKind.PublicKeyword));
propertiesClass = dBusInterface.Properties!.Aggregate(propertiesClass, static (current, property)
=> current.AddMembers(
MakeGetSetProperty(ParseTypeName(property.DotNetType), property.Name!, Token(SyntaxKind.PublicKeyword))));
propertiesClass = dBusInterface.Properties!.Aggregate(propertiesClass, static (current, property) =>
current.AddMembers(
MakeGetSetProperty(GetDotnetType(property, AccessMode.Read), Pascalize(property.Name!), Token(SyntaxKind.PublicKeyword))));
cl = cl.AddMembers(propertiesClass);
}
private void AddReadProperties(ref ClassDeclarationSyntax cl, IEnumerable<DBusProperty> dBusProperties) =>
cl = cl.AddMembers(
MethodDeclaration(ParseTypeName("Properties"), "ReadProperties")
MethodDeclaration(
IdentifierName("Properties"), "ReadProperties")
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("reader"))
.WithType(ParseTypeName("Reader"))
.WithType(IdentifierName("Reader"))
.AddModifiers(Token(SyntaxKind.RefKeyword)),
Parameter(Identifier("changed"))
.WithType(NullableType(ParseTypeName("List<string>")))
.WithType(
NullableType(
GenericName("List")
.AddTypeArgumentListArguments(
PredefinedType(Token(SyntaxKind.StringKeyword)))))
.WithDefault(
EqualsValueClause(
LiteralExpression(SyntaxKind.NullLiteralExpression))))
.WithBody(
Block(
LocalDeclarationStatement(VariableDeclaration(ParseTypeName("Properties"))
LocalDeclarationStatement(VariableDeclaration(IdentifierName("Properties"))
.AddVariables(
VariableDeclarator("props")
.WithInitializer(
EqualsValueClause(
InvocationExpression(
ObjectCreationExpression(ParseTypeName("Properties"))))))),
LocalDeclarationStatement(VariableDeclaration(ParseTypeName("ArrayEnd"))
ObjectCreationExpression(IdentifierName("Properties"))))))),
LocalDeclarationStatement(VariableDeclaration(IdentifierName("ArrayEnd"))
.AddVariables(
VariableDeclarator("headersEnd")
.WithInitializer(
@ -395,28 +465,30 @@ namespace Tmds.DBus.SourceGenerator
MakeMemberAccessExpression("reader", "ReadString")))
.WithSections(
List(
dBusProperties.Select(x => SwitchSection()
dBusProperties.Select(property => SwitchSection()
.AddLabels(
CaseSwitchLabel(
MakeLiteralExpression(x.Name!)))
MakeLiteralExpression(property.Name!)))
.AddStatements(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("reader", "ReadSignature"))
.AddArgumentListArguments(
Argument(MakeLiteralExpression(x.Type!)))),
Argument(
MakeLiteralExpression(property.Type!)))),
ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
MakeMemberAccessExpression("props", x.Name!),
MakeMemberAccessExpression("props", Pascalize(property.Name!)),
InvocationExpression(
MakeMemberAccessExpression("reader", GetOrAddReadMethod(x))))),
MakeMemberAccessExpression("reader", GetOrAddReadMethod(property))))),
ExpressionStatement(
ConditionalAccessExpression(
IdentifierName("changed"), InvocationExpression(
MemberBindingExpression(
IdentifierName("Add")))
.AddArgumentListArguments(
Argument(MakeLiteralExpression(x.Name!))))),
Argument(
MakeLiteralExpression(Pascalize(property.Name!)))))),
BreakStatement())))))),
ReturnStatement(IdentifierName("props")))));
@ -426,7 +498,7 @@ namespace Tmds.DBus.SourceGenerator
InvocationExpression(
MakeMemberAccessExpression("_connection", "CallMethodAsync"))
.WithArgumentList(args)),
LocalFunctionStatement(ParseTypeName("MessageBuffer"), "CreateMessage")
LocalFunctionStatement(IdentifierName("MessageBuffer"), "CreateMessage")
.WithBody(createMessageBody));
private static BlockSyntax MakeCreateMessageBody(ExpressionSyntax interfaceExpression, string methodName, string? signature, params StatementSyntax[] statements)
@ -443,7 +515,7 @@ namespace Tmds.DBus.SourceGenerator
return Block(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageWriter"),
VariableDeclaration(IdentifierName("MessageWriter"),
SingletonSeparatedList(
VariableDeclarator("writer")
.WithInitializer(EqualsValueClause(
@ -456,7 +528,7 @@ namespace Tmds.DBus.SourceGenerator
.AddStatements(statements)
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("MessageBuffer"))
VariableDeclaration(IdentifierName("MessageBuffer"))
.AddVariables(
VariableDeclarator("message")
.WithInitializer(

270
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Utils.cs

@ -22,6 +22,7 @@ namespace Tmds.DBus.SourceGenerator
UsingDirective(IdentifierName("System.Runtime.InteropServices")),
UsingDirective(IdentifierName("System.Threading")),
UsingDirective(IdentifierName("System.Threading.Tasks")),
UsingDirective(IdentifierName("Microsoft.Win32.SafeHandles")),
UsingDirective(IdentifierName("Tmds.DBus.Protocol")))
.AddMembers(namespaceDeclaration
.WithLeadingTrivia(
@ -72,104 +73,8 @@ namespace Tmds.DBus.SourceGenerator
private static LiteralExpressionSyntax MakeLiteralExpression(string literal) => LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(literal));
private static LiteralExpressionSyntax MakeLiteralExpression(int literal) => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(literal));
private static SyntaxToken Utf8Literal(string value) => Token(TriviaList(ElasticMarker), SyntaxKind.Utf8StringLiteralToken, SymbolDisplay.FormatLiteral(value, true) + "u8", value, TriviaList(ElasticMarker));
private static string TupleOf(IEnumerable<string> elements) => $"({string.Join(", ", elements)})";
private static ExpressionSyntax MakeGetDBusVariantExpression(DBusValue dBusValue, ExpressionSyntax accessValueExpression) =>
dBusValue.DBusType switch
{
DBusType.Byte => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusByteItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Bool => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusBoolItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Int16 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusInt16Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.UInt16 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusUInt16Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Int32 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusInt32Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.UInt32 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusUInt32Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Int64 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusInt64Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.UInt64 => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusUInt64Item")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Double => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusDoubleItem"))).
AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.String => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusStringItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.ObjectPath => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusObjectPathItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Signature => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusSignatureItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Variant => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusVariantItem")))
.AddArgumentListArguments(Argument(accessValueExpression)),
DBusType.Array => ObjectCreationExpression(
ParseTypeName("DBusArrayItem"))
.AddArgumentListArguments(
Argument(MakeMemberAccessExpression("DBusType", Enum.GetName(typeof(DBusType), dBusValue.InnerDBusTypes![0].DBusType)!)),
Argument(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, accessValueExpression, IdentifierName("Select")))
.AddArgumentListArguments(
Argument(
SimpleLambdaExpression(
Parameter(
Identifier("x")))
.WithExpressionBody(
MakeGetDBusVariantExpression(dBusValue.InnerDBusTypes![0], IdentifierName("x"))))), IdentifierName("ToArray"))))),
DBusType.DictEntry => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusDictEntryItem")))
.AddArgumentListArguments(
Argument(
MakeGetDBusVariantExpression(dBusValue.InnerDBusTypes![0], MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, accessValueExpression, IdentifierName("Key")))),
Argument(
MakeGetDBusVariantExpression(dBusValue.InnerDBusTypes![1], MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, accessValueExpression, IdentifierName("Value"))))),
DBusType.Struct => InvocationExpression(
ObjectCreationExpression(
ParseTypeName("DBusStructItem")))
.AddArgumentListArguments(
Argument(
ObjectCreationExpression(ParseTypeName("DBusItem[]"))
.WithInitializer(
InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList(
dBusValue.InnerDBusTypes!.Select((x, i) =>
MakeGetDBusVariantExpression(x, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, accessValueExpression, IdentifierName($"Item{i + 1}"))))))))),
_ => throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType, "")
};
private string GetOrAddWriteMethod(DBusValue dBusValue) =>
dBusValue.DBusType switch
{
@ -182,15 +87,15 @@ namespace Tmds.DBus.SourceGenerator
DBusType.Int64 => "WriteInt64",
DBusType.UInt64 => "WriteUInt64",
DBusType.Double => "WriteDouble",
DBusType.String => "WriteString",
DBusType.ObjectPath => "WriteObjectPath",
DBusType.String => "WriteNullableString",
DBusType.ObjectPath => "WriteObjectPathSafe",
DBusType.Signature => "WriteSignature",
DBusType.UnixFd => "WriteHandle",
DBusType.Variant => "WriteDBusVariant",
DBusType.Variant => "WriteVariant",
DBusType.Array => GetOrAddWriteArrayMethod(dBusValue),
DBusType.DictEntry => GetOrAddWriteDictionaryMethod(dBusValue),
DBusType.Struct => GetOrAddWriteStructMethod(dBusValue),
_ => throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType, "")
_ => throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType, null)
};
private string GetOrAddReadMethod(DBusValue dBusValue) =>
@ -208,12 +113,12 @@ namespace Tmds.DBus.SourceGenerator
DBusType.String => "ReadString",
DBusType.ObjectPath => "ReadObjectPath",
DBusType.Signature => "ReadSignature",
DBusType.UnixFd => "ReadHandle",
DBusType.Variant => "ReadDBusVariant",
DBusType.UnixFd => "ReadHandle<SafeFileHandle>",
DBusType.Variant => "ReadVariantValue",
DBusType.Array => GetOrAddReadArrayMethod(dBusValue),
DBusType.DictEntry => GetOrAddReadDictionaryMethod(dBusValue),
DBusType.Struct => GetOrAddReadStructMethod(dBusValue),
_ => throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType, "")
_ => throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType, null)
};
private string GetOrAddReadArrayMethod(DBusValue dBusValue)
@ -223,24 +128,26 @@ namespace Tmds.DBus.SourceGenerator
return identifier;
_readMethodExtensions.Add(identifier,
MethodDeclaration(ParseTypeName(dBusValue.DotNetType), identifier)
MethodDeclaration(GetDotnetType(dBusValue, AccessMode.Read), identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("reader"))
.WithType(ParseTypeName("Reader"))
.WithType(IdentifierName("Reader"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)))
.WithBody(
Block()
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName($"List<{dBusValue.InnerDBusTypes![0].DotNetType}>"))
VariableDeclaration(GenericName("List")
.AddTypeArgumentListArguments(
GetDotnetType(dBusValue.InnerDBusTypes![0], AccessMode.Read)))
.AddVariables(
VariableDeclarator("items")
.WithInitializer(
EqualsValueClause(
ImplicitObjectCreationExpression())))),
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("ArrayEnd"))
VariableDeclaration(IdentifierName("ArrayEnd"))
.AddVariables(
VariableDeclarator("headersEnd")
.WithInitializer(
@ -277,24 +184,24 @@ namespace Tmds.DBus.SourceGenerator
return identifier;
_readMethodExtensions.Add(identifier,
MethodDeclaration(ParseTypeName(dBusValue.DotNetType), identifier)
MethodDeclaration(GetDotnetType(dBusValue, AccessMode.Read), identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("reader"))
.WithType(ParseTypeName("Reader"))
.WithType(IdentifierName("Reader"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)))
.WithBody(
Block()
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName(dBusValue.DotNetType))
VariableDeclaration(GetDotnetType(dBusValue, AccessMode.Read))
.AddVariables(
VariableDeclarator("items")
.WithInitializer(
EqualsValueClause(
ImplicitObjectCreationExpression())))),
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("ArrayEnd"))
VariableDeclaration(IdentifierName("ArrayEnd"))
.AddVariables(
VariableDeclarator("headersEnd")
.WithInitializer(
@ -332,11 +239,11 @@ namespace Tmds.DBus.SourceGenerator
return identifier;
_readMethodExtensions.Add(identifier,
MethodDeclaration(ParseTypeName(dBusValue.DotNetType), identifier)
MethodDeclaration(GetDotnetType(dBusValue, AccessMode.Read), identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("reader"))
.WithType(ParseTypeName("Reader"))
.WithType(IdentifierName("Reader"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)))
.WithBody(
Block()
@ -345,13 +252,19 @@ namespace Tmds.DBus.SourceGenerator
InvocationExpression(
MakeMemberAccessExpression("reader", "AlignStruct"))),
ReturnStatement(
InvocationExpression(
MakeMemberAccessExpression("ValueTuple", "Create"))
.AddArgumentListArguments(
dBusValue.InnerDBusTypes!.Select(
x => Argument(
dBusValue.InnerDBusTypes!.Length == 1
? ObjectCreationExpression(
GetDotnetType(dBusValue, AccessMode.Read))
.AddArgumentListArguments(
Argument(
InvocationExpression(
MakeMemberAccessExpression("reader", GetOrAddReadMethod(x))))).ToArray())))));
MakeMemberAccessExpression("reader", GetOrAddReadMethod(dBusValue.InnerDBusTypes![0])))))
: TupleExpression()
.AddArguments(
dBusValue.InnerDBusTypes!.Select(innerDBusValue => Argument(
InvocationExpression(
MakeMemberAccessExpression("reader", GetOrAddReadMethod(innerDBusValue)))))
.ToArray())))));
return identifier;
}
@ -367,7 +280,7 @@ namespace Tmds.DBus.SourceGenerator
BlockSyntax block = Block()
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("Reader"))
VariableDeclaration(IdentifierName("Reader"))
.AddVariables(
VariableDeclarator("reader")
.WithInitializer(
@ -398,7 +311,7 @@ namespace Tmds.DBus.SourceGenerator
{
block = block.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName(dBusValues[i].DotNetType))
VariableDeclaration(GetDotnetType(dBusValues[i], AccessMode.Read))
.AddVariables(
VariableDeclarator($"arg{i}")
.WithInitializer(
@ -415,11 +328,13 @@ namespace Tmds.DBus.SourceGenerator
}
_readMethodExtensions.Add(identifier,
MethodDeclaration(ParseTypeName(ParseReturnType(dBusValues)!), identifier)
MethodDeclaration(ParseReturnType(dBusValues, AccessMode.Read)!, identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("message")).WithType(ParseTypeName("Message")),
Parameter(Identifier("_")).WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
Parameter(Identifier("message"))
.WithType(IdentifierName("Message")),
Parameter(Identifier("_"))
.WithType(NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))))
.WithBody(block));
return identifier;
@ -432,19 +347,23 @@ namespace Tmds.DBus.SourceGenerator
return identifier;
_writeMethodExtensions.Add(identifier,
MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
MethodDeclaration(
PredefinedType(Token(SyntaxKind.VoidKeyword)), identifier)
.AddModifiers(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("writer"))
.WithType(ParseTypeName("MessageWriter"))
.WithType(IdentifierName("MessageWriter"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)),
Parameter(Identifier("values"))
.WithType(ParseTypeName(dBusValue.DotNetType)))
.WithType(
GetDotnetType(dBusValue, AccessMode.Write, true)))
.WithBody(
Block()
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("ArrayStart"))
VariableDeclaration(IdentifierName("ArrayStart"))
.AddVariables(
VariableDeclarator("arrayStart")
.WithInitializer(
@ -454,11 +373,21 @@ namespace Tmds.DBus.SourceGenerator
.AddArgumentListArguments(
Argument(
MakeMemberAccessExpression("DBusType", Enum.GetName(typeof(DBusType), dBusValue.InnerDBusTypes![0].DBusType)!))))))),
ForEachStatement(ParseTypeName(dBusValue.InnerDBusTypes[0].DotNetType), "value", IdentifierName("values"), ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes[0])))
.AddArgumentListArguments(
Argument(IdentifierName("value"))))),
IfStatement(
IsPatternExpression(
IdentifierName("values"),
UnaryPattern(
ConstantPattern(
LiteralExpression(SyntaxKind.NullLiteralExpression)))),
ForEachStatement(
GetDotnetType(dBusValue.InnerDBusTypes[0], AccessMode.Write, true),
"value",
IdentifierName("values"),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes[0])))
.AddArgumentListArguments(
Argument(IdentifierName("value")))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteArrayEnd"))
@ -476,18 +405,26 @@ namespace Tmds.DBus.SourceGenerator
_writeMethodExtensions.Add(identifier,
MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), identifier)
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddModifiers(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("writer"))
.WithType(ParseTypeName("MessageWriter"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)),
Parameter(Identifier("values"))
.WithType(ParseTypeName(dBusValue.DotNetType)))
Parameter(
Identifier("writer"))
.WithType(
IdentifierName("MessageWriter"))
.AddModifiers(
Token(SyntaxKind.ThisKeyword),
Token(SyntaxKind.RefKeyword)),
Parameter(
Identifier("values"))
.WithType(
GetDotnetType(dBusValue, AccessMode.Write, true)))
.WithBody(
Block()
.AddStatements(
LocalDeclarationStatement(
VariableDeclaration(ParseTypeName("ArrayStart"))
VariableDeclaration(IdentifierName("ArrayStart"))
.AddVariables(
VariableDeclarator("arrayStart")
.WithInitializer(
@ -497,26 +434,41 @@ namespace Tmds.DBus.SourceGenerator
.AddArgumentListArguments(
Argument(
MakeMemberAccessExpression("DBusType", "Struct"))))))),
ForEachStatement(ParseTypeName($"KeyValuePair<{dBusValue.InnerDotNetTypes![0]}, {dBusValue.InnerDotNetTypes![1]}>"), "value", IdentifierName("values"), Block(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteStructureStart"))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes![0])))
.AddArgumentListArguments(
Argument(MakeMemberAccessExpression("value", "Key")))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes![1])))
.AddArgumentListArguments(
Argument(MakeMemberAccessExpression("value", "Value"))))
)),
IfStatement(
IsPatternExpression(
IdentifierName("values"),
UnaryPattern(
ConstantPattern(
LiteralExpression(SyntaxKind.NullLiteralExpression)))),
ForEachStatement(
GenericName("KeyValuePair")
.AddTypeArgumentListArguments(
GetDotnetType(dBusValue.InnerDBusTypes![0], AccessMode.Write),
GetDotnetType(dBusValue.InnerDBusTypes![1], AccessMode.Write, true)),
"value",
IdentifierName("values"),
Block(
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteStructureStart"))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes![0])))
.AddArgumentListArguments(
Argument(
MakeMemberAccessExpression("value", "Key")))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", GetOrAddWriteMethod(dBusValue.InnerDBusTypes![1])))
.AddArgumentListArguments(
Argument(
MakeMemberAccessExpression("value", "Value"))))))),
ExpressionStatement(
InvocationExpression(
MakeMemberAccessExpression("writer", "WriteArrayEnd"))
.AddArgumentListArguments(
Argument(IdentifierName("arrayStart")))))));
Argument(
IdentifierName("arrayStart")))))));
return identifier;
}
@ -532,10 +484,10 @@ namespace Tmds.DBus.SourceGenerator
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(
Parameter(Identifier("writer"))
.WithType(ParseTypeName("MessageWriter"))
.WithType(IdentifierName("MessageWriter"))
.AddModifiers(Token(SyntaxKind.ThisKeyword), Token(SyntaxKind.RefKeyword)),
Parameter(Identifier("value"))
.WithType(ParseTypeName(dBusValue.DotNetType)))
.WithType(GetDotnetType(dBusValue, AccessMode.Write, true)))
.WithBody(
Block(
ExpressionStatement(

27
src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.cs

@ -59,7 +59,9 @@ namespace Tmds.DBus.SourceGenerator
foreach (DBusInterface dBusInterface in value.Node.Interfaces!)
{
TypeDeclarationSyntax typeDeclarationSyntax = GenerateProxy(dBusInterface);
NamespaceDeclarationSyntax namespaceDeclaration = NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator")).AddMembers(typeDeclarationSyntax);
NamespaceDeclarationSyntax namespaceDeclaration = NamespaceDeclaration(
IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(typeDeclarationSyntax);
CompilationUnitSyntax compilationUnit = MakeCompilationUnit(namespaceDeclaration);
productionContext.AddSource($"Tmds.DBus.SourceGenerator.{Pascalize(dBusInterface.Name!)}.g.cs", compilationUnit.GetText(Encoding.UTF8));
}
@ -68,7 +70,9 @@ namespace Tmds.DBus.SourceGenerator
foreach (DBusInterface dBusInterface in value.Node.Interfaces!)
{
TypeDeclarationSyntax typeDeclarationSyntax = GenerateHandler(dBusInterface);
NamespaceDeclarationSyntax namespaceDeclaration = NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator")).AddMembers(typeDeclarationSyntax);
NamespaceDeclarationSyntax namespaceDeclaration = NamespaceDeclaration(
IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(typeDeclarationSyntax);
CompilationUnitSyntax compilationUnit = MakeCompilationUnit(namespaceDeclaration);
productionContext.AddSource($"Tmds.DBus.SourceGenerator.{Pascalize(dBusInterface.Name!)}.g.cs", compilationUnit.GetText(Encoding.UTF8));
}
@ -76,25 +80,32 @@ namespace Tmds.DBus.SourceGenerator
}
}
CompilationUnitSyntax readerExtensions = MakeCompilationUnit(NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator"))
CompilationUnitSyntax readerExtensions = MakeCompilationUnit(
NamespaceDeclaration(
IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(
ClassDeclaration("ReaderExtensions")
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))
.WithMembers(
List<MemberDeclarationSyntax>(_readMethodExtensions.Values))));
CompilationUnitSyntax writerExtensions = MakeCompilationUnit(NamespaceDeclaration(IdentifierName("Tmds.DBus.SourceGenerator"))
CompilationUnitSyntax writerExtensions = MakeCompilationUnit(
NamespaceDeclaration(
IdentifierName("Tmds.DBus.SourceGenerator"))
.AddMembers(
ClassDeclaration("WriterExtensions")
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))
.WithMembers(
List<MemberDeclarationSyntax>(_writeMethodExtensions.Values))));
List<MemberDeclarationSyntax>(_writeMethodExtensions.Values)
.Add(MakeWriteNullableStringMethod())
.Add(MakeWriteObjectPathSafeMethod()))));
productionContext.AddSource("Tmds.DBus.SourceGenerator.PropertyChanges.cs", MakePropertyChangesClass().GetText(Encoding.UTF8));
productionContext.AddSource("Tmds.DBus.SourceGenerator.SignalHelper.cs", MakeSignalHelperClass().GetText(Encoding.UTF8));
productionContext.AddSource("Tmds.DBus.SourceGenerator.PropertyChanges.cs", PropertyChangesClass);
productionContext.AddSource("Tmds.DBus.SourceGenerator.SignalHelper.cs", SignalHelperClass);
productionContext.AddSource("Tmds.DBus.SourceGenerator.PathHandler.cs", PathHandlerClass);
productionContext.AddSource("Tmds.DBus.SourceGenerator.IDBusInterfaceHandler.cs", DBusInterfaceHandlerInterface);
productionContext.AddSource("Tmds.DBus.SourceGenerator.ReaderExtensions.cs", readerExtensions.GetText(Encoding.UTF8));
productionContext.AddSource("Tmds.DBus.SourceGenerator.WriterExtensions.cs", writerExtensions.GetText(Encoding.UTF8));
productionContext.AddSource("Tmds.DBus.SourceGenerator.VariantExtensions.cs", VariantExtensions);
});
}
}

20
src/Linux/Tmds.DBus.SourceGenerator/SignatureReader.cs

@ -28,16 +28,12 @@ namespace Tmds.DBus.SourceGenerator
if (length > 1)
{
switch (type)
innerSignature = type switch
{
case DBusType.Array:
innerSignature = _signature.Slice(1, length - 1);
break;
case DBusType.Struct:
case DBusType.DictEntry:
innerSignature = _signature.Slice(1, length - 2);
break;
}
DBusType.Array => _signature.Slice(1, length - 1),
DBusType.Struct or DBusType.DictEntry => _signature.Slice(1, length - 2),
_ => innerSignature
};
}
_signature = _signature.Slice(length);
@ -158,12 +154,12 @@ namespace Tmds.DBus.SourceGenerator
signature = signature.Slice(1);
T keyType = Transform(keySignature, map);
T valueType = Transform(valueSignature, map);
return map(DBusType.DictEntry, new[] { keyType, valueType });
return map(DBusType.DictEntry, [keyType, valueType]);
case DBusType.Array:
signature = signature.Slice(1);
T elementType = Transform(signature, map);
//signature = signature.Slice(1);
return map(DBusType.Array, new[] { elementType });
return map(DBusType.Array, [elementType]);
case DBusType.Struct:
signature = signature.Slice(1, signature.Length - 2);
int typeCount = CountTypes(signature);
@ -176,7 +172,7 @@ namespace Tmds.DBus.SourceGenerator
return map(DBusType.Struct, innerTypes);
default:
return map(dbusType, Array.Empty<T>());
return map(dbusType, []);
}
}

8
src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj

@ -9,5 +9,13 @@
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Remove="Properties\launchSettings.json" />
</ItemGroup>
</Project>

194
src/Linux/Tmds.DBus/Address.cs

@ -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);
}
}
}

301
src/Linux/Tmds.DBus/AddressEntry.cs

@ -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");
}
}
}
}

37
src/Linux/Tmds.DBus/ArgumentAttribute.cs

@ -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";
}
}
}

65
src/Linux/Tmds.DBus/ClientConnectionOptions.cs

@ -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; }
}
}

35
src/Linux/Tmds.DBus/ClientSetupResult.cs

@ -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; }
}
}

45
src/Linux/Tmds.DBus/CloseSafeHandle.cs

@ -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);
}
}

261
src/Linux/Tmds.DBus/CodeGen/ArgTypeInspector.cs

@ -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);
}
}
}
}
}

23
src/Linux/Tmds.DBus/CodeGen/ArgumentDescription.cs

@ -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; }
}
}

310
src/Linux/Tmds.DBus/CodeGen/DBusAdapter.cs

@ -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;
}
}
}
}
}

417
src/Linux/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs

@ -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();
}
}
}

145
src/Linux/Tmds.DBus/CodeGen/DBusObjectProxy.cs

@ -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;
}
}
}

276
src/Linux/Tmds.DBus/CodeGen/DBusObjectProxyTypeBuilder.cs

@ -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);
}
}
}

115
src/Linux/Tmds.DBus/CodeGen/DynamicAssembly.cs

@ -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;
}
}
}
}

62
src/Linux/Tmds.DBus/CodeGen/InterfaceDescription.cs

@ -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>(); } }
}
}

39
src/Linux/Tmds.DBus/CodeGen/MethodDescription.cs

@ -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; }
}
}

23
src/Linux/Tmds.DBus/CodeGen/PropertyDescription.cs

@ -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; }
}
}

38
src/Linux/Tmds.DBus/CodeGen/PropertyTypeInspector.cs

@ -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);
}
}
}
}

162
src/Linux/Tmds.DBus/CodeGen/ReadMethodFactory.cs

@ -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));
}
}

35
src/Linux/Tmds.DBus/CodeGen/SignalDescription.cs

@ -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; }
}
}

79
src/Linux/Tmds.DBus/CodeGen/TypeBuilderExtensions.cs

@ -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;
}
}
}

449
src/Linux/Tmds.DBus/CodeGen/TypeDescription.cs

@ -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);
}
}
}

23
src/Linux/Tmds.DBus/CodeGen/TypeExtensions.cs

@ -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();
}
}
}

157
src/Linux/Tmds.DBus/CodeGen/WriteMethodFactory.cs

@ -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));
}
}

29
src/Linux/Tmds.DBus/ConnectException.cs

@ -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)
{ }
}
}

1063
src/Linux/Tmds.DBus/Connection2.cs

File diff suppressed because it is too large

31
src/Linux/Tmds.DBus/ConnectionInfo.cs

@ -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);
}
}

24
src/Linux/Tmds.DBus/ConnectionOptions.cs

@ -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; }
}
}

23
src/Linux/Tmds.DBus/ConnectionState.cs

@ -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
}
}

48
src/Linux/Tmds.DBus/ConnectionStateChangedEventArgs.cs

@ -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; }
}
}

1059
src/Linux/Tmds.DBus/DBusConnection.cs

File diff suppressed because it is too large

38
src/Linux/Tmds.DBus/DBusException.cs

@ -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; }
}
}

59
src/Linux/Tmds.DBus/DBusInterfaceAttribute.cs

@ -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;
}
}
}

15
src/Linux/Tmds.DBus/DictionaryAttribute.cs

@ -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
{}
}

18
src/Linux/Tmds.DBus/DisconnectedException.cs

@ -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)
{ }
}
}

77
src/Linux/Tmds.DBus/Environment.cs

@ -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;
}
}
}
}

76
src/Linux/Tmds.DBus/IConnection.cs

@ -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);
}
}

19
src/Linux/Tmds.DBus/IDBus.cs

@ -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();
}
}

17
src/Linux/Tmds.DBus/IDBusObject.cs

@ -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; }
}
}

52
src/Linux/Tmds.DBus/Interop.cs

@ -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);
}
}

10
src/Linux/Tmds.DBus/MessageHandler.cs

@ -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);
}

62
src/Linux/Tmds.DBus/MessageHelper.cs

@ -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;
}
}
}

179
src/Linux/Tmds.DBus/ObjectPath2.cs

@ -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);
}
}
}
}

76
src/Linux/Tmds.DBus/OwnerChangedMatchRule.cs

@ -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();
}
}
}

25
src/Linux/Tmds.DBus/PropertyAccess.cs

@ -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
}
}

22
src/Linux/Tmds.DBus/PropertyAttribute.cs

@ -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;
}
}

85
src/Linux/Tmds.DBus/PropertyChanges.cs

@ -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?);
}
}
}

35
src/Linux/Tmds.DBus/Protocol/DType.cs

@ -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)'}',
}
}

14
src/Linux/Tmds.DBus/Protocol/EndianFlag.cs

@ -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',
}
}

22
src/Linux/Tmds.DBus/Protocol/FieldCode.cs

@ -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
}
}

180
src/Linux/Tmds.DBus/Protocol/Header.cs

@ -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
}
}

17
src/Linux/Tmds.DBus/Protocol/HeaderFlag.cs

@ -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,
}
}

16
src/Linux/Tmds.DBus/Protocol/IMessageStream.cs

@ -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);
}
}

11
src/Linux/Tmds.DBus/Protocol/IProxyFactory.cs

@ -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);
}
}

163
src/Linux/Tmds.DBus/Protocol/IntrospectionWriter.cs

@ -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();
}
}
}

33
src/Linux/Tmds.DBus/Protocol/Message.cs

@ -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;
}
}

656
src/Linux/Tmds.DBus/Protocol/MessageReader.cs

@ -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; }
}
}
}

25
src/Linux/Tmds.DBus/Protocol/MessageType.cs

@ -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
}
}

608
src/Linux/Tmds.DBus/Protocol/MessageWriter.cs

@ -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);
}
}
}

11
src/Linux/Tmds.DBus/Protocol/MethodHandler.cs

@ -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);
}

177
src/Linux/Tmds.DBus/Protocol/ProtocolInformation.cs

@ -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);
}
}
}
}

10
src/Linux/Tmds.DBus/Protocol/SignalHandler.cs

@ -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);
}

26
src/Linux/Tmds.DBus/Protocol/UnixFd.cs

@ -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;
}
}
}

21
src/Linux/Tmds.DBus/ProtocolException.cs

@ -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)
{}
}
}

13
src/Linux/Tmds.DBus/ReleaseNameReply.cs

@ -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
}
}

17
src/Linux/Tmds.DBus/RequestNameOptions.cs

@ -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,
}
}

14
src/Linux/Tmds.DBus/RequestNameReply.cs

@ -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,
}
}

63
src/Linux/Tmds.DBus/ServerConnectionOptions.cs

@ -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;
}
}
}
}

22
src/Linux/Tmds.DBus/ServerStartOptions.cs

@ -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; }
}
}

45
src/Linux/Tmds.DBus/ServiceOwnerChangedEventArgs.cs

@ -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; }
}
}

24
src/Linux/Tmds.DBus/ServiceRegistrationOptions.cs

@ -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
}
}

17
src/Linux/Tmds.DBus/ServiceStartResult.cs

@ -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
}
}

83
src/Linux/Tmds.DBus/SignalMatchRule.cs

@ -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('\'');
}
}
}

63
src/Linux/Tmds.DBus/SignalWatcher.cs

@ -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));
}
}
}

899
src/Linux/Tmds.DBus/Signature.cs

@ -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;
}
}
}
}

7
src/Linux/Tmds.DBus/Tmds.DBus.csproj

@ -1,7 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

191
src/Linux/Tmds.DBus/Transports/LocalServer.cs

@ -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;
}
}

433
src/Linux/Tmds.DBus/Transports/Transport.cs

@ -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();
}
}
}

525
src/Linux/Tmds.DBus/Transports/TransportSocket.cs

@ -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);
}
}

91
src/Linux/Tmds.DBus/Transports/UnixDomainSocketEndPoint.cs

@ -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;
}
}

127
src/Linux/Tmds.DBus/WrappedDisposable.cs

@ -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…
Cancel
Save