diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf index 914a74c27e..b32d6e3d70 100644 --- a/Avalonia.Desktop.slnf +++ b/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", diff --git a/Avalonia.sln b/Avalonia.sln index 8c4ad8b03e..36f88e3592 100644 --- a/Avalonia.sln +++ b/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} diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index 168ad8b82a..995c57dc64 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -20,8 +20,6 @@ OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all"/> - - diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs index 94709926e7..8cb5d5761c 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs @@ -45,7 +45,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx public ValueTask WatchForwardKeyAsync(Action 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(Disposable.Empty); public ValueTask WatchUpdateFormattedPreeditAsync( @@ -53,7 +53,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx _old?.WatchUpdateFormattedPreeditAsync(handler!) ?? _modern?.WatchUpdateFormattedPreeditAsync(handler!) ?? new ValueTask(Disposable.Empty); - + public Task SetCapacityAsync(uint flags) => _old?.SetCapacityAsync(flags) ?? _modern?.SetCapabilityAsync(flags) ?? Task.CompletedTask; } diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs index efd173d9a6..eaa5c8f13e 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs +++ b/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 diff --git a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs index 35dc18288a..d81d2d7394 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs +++ b/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; diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index fcc118bd31..beb949536f 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/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(); + Version = 4; } protected override Connection Connection { get; } public override string Path { get; } - protected override ValueTask<(uint revision, (int, Dictionary, DBusVariantItem[]) layout)> OnGetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames) + protected override ValueTask<(uint Revision, (int, Dictionary, 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, DBusVariantItem[]))>((_revision, layout)); + return new ValueTask<(uint, (int, Dictionary, Variant[]))>((_revision, layout)); } - protected override ValueTask<(int, Dictionary)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames) + protected override ValueTask<(int, Dictionary)[]> OnGetGroupPropertiesAsync(int[] ids, string[] propertyNames) => new(ids.Select(id => (id, GetProperties(GetMenu(id), propertyNames))).ToArray()); - protected override ValueTask OnGetPropertyAsync(int id, string name) => - new(GetProperty(GetMenu(id), name) ?? new DBusVariantItem("i", new DBusInt32Item(0))); + protected override ValueTask 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 OnEventGroupAsync((int, string, DBusVariantItem, uint)[] events) + protected override ValueTask 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 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(), Array.Empty())); 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 ?? "")); + return new Variant(item.Header ?? ""); 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(); + var lst = new Array(); 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>(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(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 GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names) + private static Dictionary GetProperties((NativeMenuItemBase? item, NativeMenu? menu) i, string[] names) { if (names.Length == 0) names = s_allProperties; - var properties = new Dictionary(); + var properties = new Dictionary(); 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, DBusVariantItem[]) GetLayout(NativeMenuItemBase? item, NativeMenu? menu, int depth, string[] propertyNames) + private (int, Dictionary, 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() : new DBusVariantItem[menu.Items.Count]; + var children = depth == 0 || menu is null ? Array.Empty() : 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(layout.Item2), new Array(layout.Item3))); } } diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 852ed90764..acfa985e90 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/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)); diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index 7ee963fc17..bd041a044a 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -56,14 +56,15 @@ namespace Avalonia.FreeDesktop { var parentWindow = $"x11:{_handle.Handle:X}"; ObjectPath objectPath; - var chooserOptions = new Dictionary(); - var filters = ParseFilters(options.FileTypeFilter); - if (filters is not null) + var chooserOptions = new Dictionary(); + + 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(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()); }); var uris = await tsc.Task ?? Array.Empty(); @@ -85,15 +86,14 @@ namespace Avalonia.FreeDesktop { var parentWindow = $"x11:{_handle.Handle:X}"; ObjectPath objectPath; - var chooserOptions = new Dictionary(); - var filters = ParseFilters(options.FileTypeChoices); - if (filters is not null) + var chooserOptions = new Dictionary(); + 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(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(); + var mimeTypes = new List(); + var types = currentFilter.GetItem(1).GetArray(); + foreach(var t in types) { - var name = (currentFilter[0] as DBusStringItem)?.Value.ToString() ?? ""; - selectedType = new FilePickerFileType(name); - if(currentFilter[1] is DBusArrayItem types) - { - List filters = new List(); - List mimeTypes = new List(); - 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()); } }); @@ -157,16 +147,16 @@ namespace Avalonia.FreeDesktop return Array.Empty(); var parentWindow = $"x11:{_handle.Handle:X}"; - var chooserOptions = new Dictionary + var chooserOptions = new Dictionary { - { "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(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()); }); var uris = await tsc.Task ?? Array.Empty(); @@ -187,40 +177,35 @@ namespace Avalonia.FreeDesktop .Select(static path => new BclStorageFolder(new DirectoryInfo(path))).ToList(); } - private static DBusVariantItem? ParseFilters(IReadOnlyList? fileTypes) + private static bool TryParseFilters(IReadOnlyList? 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(); + var filters = new Array>>>(); foreach (var fileType in fileTypes) { - var extensions = new List(); + var extensions = new List>(); 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>(extensions))); } - return filters.Count > 0 ? new DBusVariantItem("a(sa(us))", new DBusArrayItem(DBusType.Struct, filters)) : null; + result = Variant.FromArray(filters); + return true; } } } diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index f7e95b83d0..8427a254c0 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/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(); } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/AccessMode.cs b/src/Linux/Tmds.DBus.SourceGenerator/AccessMode.cs new file mode 100644 index 0000000000..9348eca1d4 --- /dev/null +++ b/src/Linux/Tmds.DBus.SourceGenerator/AccessMode.cs @@ -0,0 +1,8 @@ +namespace Tmds.DBus.SourceGenerator +{ + public enum AccessMode + { + Read, + Write + } +} diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusInterface.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusInterface.cs index 07df444e97..24f1062da0 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusInterface.cs +++ b/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; } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Classes.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Classes.cs index efde878c52..513a56e0da 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Classes.cs +++ b/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"), "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")), - 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"), - 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"), "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")), - Parameter(Identifier("handler")) - .WithType(ParseTypeName("Action")), - 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"), - 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"), "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>")), - Parameter(Identifier("handler")) - .WithType(ParseTypeName("Action>")), - 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; - -// -#pragma warning disable -#nullable enable -namespace Tmds.DBus.SourceGenerator -{ - internal static class VariantReader - { - public static DBusVariantItem ReadDBusVariant(this ref Reader reader) - { - ReadOnlySpan signature = reader.ReadSignature(); - SignatureReader signatureReader = new(signature); - if (!signatureReader.TryRead(out DBusType dBusType, out ReadOnlySpan 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 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 innerArraySignature)) - throw new InvalidOperationException("Failed to deserialize array item"); - List 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 _) || - !innerSignatureReader.TryRead(out DBusType innerValueType, out ReadOnlySpan 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 items = new(); - SignatureReader innerSignatureReader = new(innerSignature); - while (innerSignatureReader.TryRead(out DBusType innerDBusType, out ReadOnlySpan 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 - { - private readonly IReadOnlyList _value; - - public DBusArrayItem(DBusType arrayType, IReadOnlyList value) - { - ArrayType = arrayType; - _value = value; - } - - public DBusType ArrayType { get; } - - public IEnumerator 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 - { - private readonly IReadOnlyList _value; - - public DBusStructItem(IReadOnlyList value) - { - _value = value; - } - - public IEnumerator 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 - { - private readonly IReadOnlyList _value; - - public DBusByteArrayItem(IReadOnlyList value) - { - _value = value; - } - - public IEnumerator 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; + + // + #pragma warning disable + #nullable enable + namespace Tmds.DBus.SourceGenerator + { + internal static class SignalHelper + { + public static ValueTask WatchSignalAsync(Connection connection, MatchRule rule, Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => connection.AddMatchAsync(rule, static (_, _) => null !, static (Exception? e, object _, object? _, object? handlerState) => ((Action)handlerState!).Invoke(e), null, handler, emitOnCapturedContext, flags); + + public static ValueTask WatchSignalAsync(Connection connection, MatchRule rule, MessageValueReader reader, Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => connection.AddMatchAsync(rule, reader, static (e, arg, _, handlerState) => ((Action)handlerState!).Invoke(e, arg), null, handler, emitOnCapturedContext, flags); + + public static ValueTask WatchPropertiesChangedAsync(Connection connection, string destination, string path, string @interface, MessageValueReader> reader, Action> 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 = """ + // + #pragma warning disable + #nullable enable + namespace Tmds.DBus.SourceGenerator + { + internal record PropertyChanges(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; + + // + #pragma warning disable + #nullable enable + namespace Tmds.DBus.SourceGenerator + { + internal interface IDBusInterfaceHandler + { + PathHandler? PathHandler { get; set; } + + Connection Connection { get; } + + string InterfaceName { get; } + + ReadOnlyMemory 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; + + // + #pragma warning disable + #nullable enable + namespace Tmds.DBus.SourceGenerator + { + internal class PathHandler : IMethodHandler + { + private readonly bool _runMethodHandlerSynchronously; + private readonly ICollection _dbusInterfaces; + + public PathHandler(string path, bool runMethodHandlerSynchronously = true) + { + Path = path; + _runMethodHandlerSynchronously = runMethodHandlerSynchronously; + _dbusInterfaces = new List(); + } + + /// + public string Path { get; } + + /// + 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); + } + + /// + public bool RunMethodHandlerSynchronously(Message message) => _runMethodHandlerSynchronously; + + /// + public void Add(IDBusInterfaceHandler item) + { + item.PathHandler = this; + _dbusInterfaces.Add(item); + } + + /// + public bool Contains(IDBusInterfaceHandler item) => _dbusInterfaces.Contains(item); + + /// + public bool Remove(IDBusInterfaceHandler item) + { + item.PathHandler = null; + return _dbusInterfaces.Remove(item); + } + + /// + public int Count => _dbusInterfaces.Count; + } + } + """; } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Handler.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Handler.cs index a49357685b..64572b32f3 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Handler.cs +++ b/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 argFields = List(); + 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")) - .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())))))), + 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())))))))); + 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")) - .AddVariables( - VariableDeclarator("dict") - .WithInitializer( - EqualsValueClause( - ObjectCreationExpression(ParseTypeName("Dictionary")) - .WithInitializer( - InitializerExpression(SyntaxKind.CollectionInitializerExpression) - .WithExpressions( - SeparatedList( - 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")) - .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}"))))); } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Parsing.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Parsing.cs index c4942fcca4..f1cc2f8457 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Parsing.cs +++ b/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? 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? dBusValues) => dBusValues?.Count switch + private static TypeSyntax? ParseReturnType(IReadOnlyList? 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? dBusValues, AccessMode accessMode) => dBusValues?.Count switch + { + 0 or null => IdentifierName("Task"), + _ => GenericName("Task") + .AddTypeArgumentListArguments( + ParseReturnType(dBusValues, accessMode)!) + }; - private static string ParseTaskReturnType(IReadOnlyList? dBusValues) => dBusValues?.Count switch + private static TypeSyntax ParseValueTaskReturnType(IReadOnlyList? 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 inArgs) => ParameterList( + private static TypeSyntax ParseTaskCompletionSourceType(IReadOnlyList? 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 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}"); + } + } } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Proxy.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Proxy.cs index a04721b638..49b6ff5c78 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Proxy.cs +++ b/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() - .ToArray() ?? Array.Empty(); + .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"))) + .WithType( + GenericName("Action") + .AddTypeArgumentListArguments( + NullableType( + IdentifierName("Exception")), + returnType))) : parameters.AddParameters( Parameter(Identifier("handler")) - .WithType(ParseTypeName("Action"))); + .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"), $"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"), "WatchPropertiesChangedAsync") - .AddModifiers(Token(SyntaxKind.PublicKeyword)) - .AddParameterListParameters( - Parameter(Identifier("handler")) - .WithType(ParseTypeName("Action>")), - 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"), "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")) - .AddVariables( - VariableDeclarator("changed") - .WithInitializer( - EqualsValueClause( - ImplicitObjectCreationExpression())))), - ReturnStatement( - InvocationExpression( - ObjectCreationExpression(ParseTypeName("PropertyChanges"))) - .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"), "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 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"))) + .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( diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Utils.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Utils.cs index 98ae13a84c..7a9bb92123 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.Utils.cs +++ b/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 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", + 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( diff --git a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.cs b/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.cs index 821cda7e69..aa47701150 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/DBusSourceGenerator.cs +++ b/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(_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(_writeMethodExtensions.Values)))); + List(_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); }); } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/SignatureReader.cs b/src/Linux/Tmds.DBus.SourceGenerator/SignatureReader.cs index 25332c8870..8225897391 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/SignatureReader.cs +++ b/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()); + return map(dbusType, []); } } diff --git a/src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj b/src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj index d54309985c..8aa357fba1 100644 --- a/src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj +++ b/src/Linux/Tmds.DBus.SourceGenerator/Tmds.DBus.SourceGenerator.csproj @@ -9,5 +9,13 @@ + + + + + + + + diff --git a/src/Linux/Tmds.DBus/Address.cs b/src/Linux/Tmds.DBus/Address.cs deleted file mode 100644 index 8125bdf1f8..0000000000 --- a/src/Linux/Tmds.DBus/Address.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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 -{ - /// - /// Helper class for determining D-Bus addresses. - /// - public static class Address - { - private static bool _systemAddressResolved = false; - private static string _systemAddress = null; - private static bool _sessionAddressResolved = false; - private static string _sessionAddress = null; - - /// - /// Address of System bus. - /// - 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; - } - } - - /// - /// Address of Session bus. - /// - 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); - } - } -} diff --git a/src/Linux/Tmds.DBus/AddressEntry.cs b/src/Linux/Tmds.DBus/AddressEntry.cs deleted file mode 100644 index 372dda7c16..0000000000 --- a/src/Linux/Tmds.DBus/AddressEntry.cs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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 entries = new List(); - - foreach (string entryStr in addresses.Split(';')) - entries.Add(AddressEntry.Parse(entryStr)); - - return entries.ToArray(); - } - - public string Method; - public readonly IDictionary Properties = new Dictionary(); - public Guid Guid = Guid.Empty; - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append(Method); - sb.Append(':'); - - bool first = true; - foreach (KeyValuePair 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 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"); - } - } - } -} diff --git a/src/Linux/Tmds.DBus/ArgumentAttribute.cs b/src/Linux/Tmds.DBus/ArgumentAttribute.cs deleted file mode 100644 index 15a08d1892..0000000000 --- a/src/Linux/Tmds.DBus/ArgumentAttribute.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Indicates the method return type or signal type represents a single D-Bus argument. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false, Inherited = true)] - public sealed class ArgumentAttribute : Attribute - { - /// - /// Name of the argument. - /// - public string Name { get; } - - /// - /// Creates an instance of the ArgumentAttribute with the specified name. - /// - /// Name of the argument. - public ArgumentAttribute(string name) - { - Name = name; - } - - /// - /// Creates an instance of the ArgumentAttribute. - /// - public ArgumentAttribute() - { - Name = "value"; - } - } -} diff --git a/src/Linux/Tmds.DBus/ClientConnectionOptions.cs b/src/Linux/Tmds.DBus/ClientConnectionOptions.cs deleted file mode 100644 index 3a9221daa2..0000000000 --- a/src/Linux/Tmds.DBus/ClientConnectionOptions.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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 -{ - /// - /// Options that configure the behavior of a Connection to a remote peer. - /// - public class ClientConnectionOptions : ConnectionOptions - { - private string _address; - - /// - /// Creates a new Connection with a specific address. - /// - /// Address of the D-Bus peer. - public ClientConnectionOptions(string address) - { - if (address == null) - throw new ArgumentNullException(nameof(address)); - _address = address; - } - - /// - /// Base constructor for derived types. - /// - protected ClientConnectionOptions() - {} - - /// - /// Automatically connect and re-connect the Connection. - /// - public bool AutoConnect { get; set; } - - /// - /// Sets up tunnel/connects to the remote peer. - /// - protected internal virtual Task SetupAsync() - { - return Task.FromResult( - new ClientSetupResult - { - ConnectionAddress = _address, - SupportsFdPassing = true, - UserId = Environment.UserId - }); - } - - /// - /// Action to clean up resources created during succesfull execution of SetupAsync. - /// - protected internal virtual void Teardown(object token) - {} - - /// - /// Run Task continuations asynchronously. - /// - public bool RunContinuationsAsynchronously { get; set; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/ClientSetupResult.cs b/src/Linux/Tmds.DBus/ClientSetupResult.cs deleted file mode 100644 index 9713d1c2bf..0000000000 --- a/src/Linux/Tmds.DBus/ClientSetupResult.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; -using System.Threading; - -namespace Tmds.DBus -{ - /// - /// Result of ClientConnectionOptions.SetupAsync - /// - public class ClientSetupResult - { - /// - /// Address of the D-Bus peer. - /// - public string ConnectionAddress { get; set; } - - /// - /// Object passed to ConnectionOptions.Teardown. - /// - public object TeardownToken { get; set; } - - /// - /// Authentication User ID (Linux UID). - /// - public string UserId { get; set; } - - /// - /// Indicates whether the connection supports Fd passing. - /// - public bool SupportsFdPassing { get; set; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CloseSafeHandle.cs b/src/Linux/Tmds.DBus/CloseSafeHandle.cs deleted file mode 100644 index 708da25d8e..0000000000 --- a/src/Linux/Tmds.DBus/CloseSafeHandle.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; -using System.Runtime.InteropServices; - -namespace Tmds.DBus -{ - /// - /// Generic file descriptor SafeHandle. - /// - public class CloseSafeHandle : SafeHandle - { - /// - /// Creates a new CloseSafeHandle. - /// - /// An IntPtr object that represents the pre-existing handle to use. - /// true to reliably release the handle during the finalization phase; false to prevent reliable release. - public CloseSafeHandle(IntPtr preexistingHandle, bool ownsHandle) - : base(new IntPtr(-1), ownsHandle) - { - SetHandle(preexistingHandle); - } - - /// - /// Gets a value that indicates whether the handle is invalid. - /// - public override bool IsInvalid - { - get { return handle == new IntPtr(-1); } - } - - /// - /// When overridden in a derived class, executes the code required to free the handle. - /// - protected override bool ReleaseHandle() - { - return close(handle.ToInt32()) == 0; - } - - [DllImport("libc", SetLastError = true)] - internal static extern int close(int fd); - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/ArgTypeInspector.cs b/src/Linux/Tmds.DBus/CodeGen/ArgTypeInspector.cs deleted file mode 100644 index 97b3e1d80b..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/ArgTypeInspector.cs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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); - 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 s_valueTupleFieldComparer = new StructFieldInfoComparer(true); - private static readonly IComparer 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 - 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(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(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(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 - { - 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); - } - } - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/ArgumentDescription.cs b/src/Linux/Tmds.DBus/CodeGen/ArgumentDescription.cs deleted file mode 100644 index 0be399835f..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/ArgumentDescription.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/DBusAdapter.cs b/src/Linux/Tmds.DBus/CodeGen/DBusAdapter.cs deleted file mode 100644 index 0f25ab34e0..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/DBusAdapter.cs +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 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 _methodHandlers; - protected internal readonly object _object; - - private State _state; - private List _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(); - _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 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(); - _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 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(); - _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[] StartWatchingSignals() - { - return Array.Empty>(); - } - - 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 CreateNonVoidReply(Message methodCall, Task resultTask, Action 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 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 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; - } - } - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs b/src/Linux/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs deleted file mode 100644 index a8580cbae9..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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).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); - 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 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[signalCount]; - ilg.Emit(OpCodes.Ldc_I4, signalCount); - ilg.Emit(OpCodes.Newarr, typeof(Task)); - - var idx = 0; - foreach (var dbusInterface in interfaces) - { - IEnumerable signals = dbusInterface.Signals ?? Array.Empty(); - - 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 = 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 methods = dbusInterface.Methods ?? Array.Empty(); - - 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."); - } - - 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 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 - 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(); - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxy.cs b/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxy.cs deleted file mode 100644 index 8795eaf48a..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxy.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 WatchNonVoidSignalAsync(string iface, string member, Action error, Action action, ReadMethodDelegate 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 WatchVoidSignalAsync(string iface, string member, Action 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 CallNonVoidMethodAsync(string iface, string member, Signature? inSignature, MessageWriter writer, ReadMethodDelegate readValue) - { - var reader = await SendMethodReturnReaderAsync(iface, member, inSignature, writer).ConfigureAwait(false); - return readValue(reader); - } - - internal protected async Task CallGenericOutMethodAsync(string iface, string member, Signature? inSignature, MessageWriter writer) - { - var reader = await SendMethodReturnReaderAsync(iface, member, inSignature, writer).ConfigureAwait(false); - return reader.ReadVariantAsType(); - } - - internal protected Task CallVoidMethodAsync(string iface, string member, Signature? inSigStr, MessageWriter writer) - { - return SendMethodReturnReaderAsync(iface, member, inSigStr, writer); - } - - private async Task 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; - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxyTypeBuilder.cs b/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxyTypeBuilder.cs deleted file mode 100644 index 46dc5b5857..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/DBusObjectProxyTypeBuilder.cs +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 - 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); - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/DynamicAssembly.cs b/src/Linux/Tmds.DBus/CodeGen/DynamicAssembly.cs deleted file mode 100644 index ca2415cf3e..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/DynamicAssembly.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 _proxyTypeMap; - private readonly Dictionary _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(); - _adapterTypeMap = new Dictionary(); - } - - 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; - } - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/InterfaceDescription.cs b/src/Linux/Tmds.DBus/CodeGen/InterfaceDescription.cs deleted file mode 100644 index 31f342c6c7..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/InterfaceDescription.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 methods, IList signals, - IList 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 _methods; - public IList Methods { get { return _methods ?? Array.Empty(); } } - private IList _signals; - public IList Signals { get { return _signals ?? Array.Empty(); } } - public MethodDescription GetPropertyMethod { get; } - public MethodDescription GetAllPropertiesMethod { get; } - public MethodDescription SetPropertyMethod { get; } - public SignalDescription PropertiesChangedSignal { get; } - private IList _properties; - public IList Properties { get { return _properties ?? Array.Empty(); } } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/MethodDescription.cs b/src/Linux/Tmds.DBus/CodeGen/MethodDescription.cs deleted file mode 100644 index 589dc2bc34..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/MethodDescription.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 inArguments, Signature? inSignature, Type outType, bool isGenericOut, Signature? outSignature, IList 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 _outArguments; - public IList OutArguments { get { return _outArguments ?? Array.Empty(); } } - private IList _inArguments; - public IList InArguments { get { return _inArguments ?? Array.Empty(); } } - public Signature? InSignature { get; } - public bool IsGenericOut { get; } - - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/PropertyDescription.cs b/src/Linux/Tmds.DBus/CodeGen/PropertyDescription.cs deleted file mode 100644 index 94d9d036a0..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/PropertyDescription.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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; } - - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/PropertyTypeInspector.cs b/src/Linux/Tmds.DBus/CodeGen/PropertyTypeInspector.cs deleted file mode 100644 index 5e911e7727..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/PropertyTypeInspector.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(); - - 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); - } - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/ReadMethodFactory.cs b/src/Linux/Tmds.DBus/CodeGen/ReadMethodFactory.cs deleted file mode 100644 index 97bc93f63f..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/ReadMethodFactory.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(MessageReader reader); - - internal class ReadMethodFactory - { - public static ReadMethodDelegate CreateReadMethodDelegate() - { - var type = typeof(T); - var readMethod = CreateReadMethodForType(type); - return (ReadMethodDelegate)readMethod.CreateDelegate(typeof(ReadMethodDelegate)); - } - - 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)); - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/SignalDescription.cs b/src/Linux/Tmds.DBus/CodeGen/SignalDescription.cs deleted file mode 100644 index b2f3813c6d..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/SignalDescription.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 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 _signalArguments; - public IList SignalArguments { get { return _signalArguments ?? Array.Empty(); } } - public Type ActionType { get; } - public bool HasOnError { get; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/TypeBuilderExtensions.cs b/src/Linux/Tmds.DBus/CodeGen/TypeBuilderExtensions.cs deleted file mode 100644 index 8f6696506a..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/TypeBuilderExtensions.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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; - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/TypeDescription.cs b/src/Linux/Tmds.DBus/CodeGen/TypeDescription.cs deleted file mode 100644 index 06d9b5a977..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/TypeDescription.cs +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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); - private static readonly Type s_idbusObjectType = typeof(IDBusObject); - private static readonly Type s_emptyActionType = typeof(Action); - private static readonly Type s_exceptionActionType = typeof(Action); - 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 _interfaces; - public IList Interfaces { get { return _interfaces ?? Array.Empty(); } } - - 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 interfaces) - { - Type = type; - _interfaces = interfaces; - } - - private static TypeDescription Describe(Type type, bool isInterfaceType) - { - var interfaces = new List(); - 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(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(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(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 interfaces) - { - if (interfaces.Any(interf => interf.Name == interfaceAttribute.Name)) - { - throw new ArgumentException($"DBus interface {interfaceAttribute.Name} is inherited multiple times"); - } - - IList methods = null; - IList signals = null; - IList 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'"); - } - - 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 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'"); - } - - 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 where T is a struct with an IDictionary and an string[] field"); - } - } - else - { - signals = signals ?? new List(); - 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 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 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(); - var argumentAttribute = param.GetCustomAttribute(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'"); - } - } - 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>'"); - } - 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(); - 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()?.Access ?? PropertyAccess.ReadWrite; - var description = new PropertyDescription(propertyName, propertySignature, propertyAccess); - properties = properties ?? new List(); - 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 arguments) - { - var argumentAttribute = parameter.GetCustomAttribute(false); - bool isValueTuple; - arguments = new List(); - 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 tupleElementNames = null; - if (isValueTuple) - { - var tupleElementNamesAttribute = parameter.GetCustomAttribute(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); - } - } -} diff --git a/src/Linux/Tmds.DBus/CodeGen/TypeExtensions.cs b/src/Linux/Tmds.DBus/CodeGen/TypeExtensions.cs deleted file mode 100644 index 113965d7bb..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/TypeExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(); - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/CodeGen/WriteMethodFactory.cs b/src/Linux/Tmds.DBus/CodeGen/WriteMethodFactory.cs deleted file mode 100644 index 9a5d32cea0..0000000000 --- a/src/Linux/Tmds.DBus/CodeGen/WriteMethodFactory.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(MessageWriter writer, T value); - - internal class WriteMethodFactory - { - public static WriteMethodDelegate CreateWriteMethodDelegate() - { - var type = typeof(T); - var writeMethod = CreateWriteMethodForType(type, true); - return (WriteMethodDelegate)writeMethod.CreateDelegate(typeof(WriteMethodDelegate)); - } - - 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)); - } -} diff --git a/src/Linux/Tmds.DBus/ConnectException.cs b/src/Linux/Tmds.DBus/ConnectException.cs deleted file mode 100644 index 3d05531787..0000000000 --- a/src/Linux/Tmds.DBus/ConnectException.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Exception thrown when the D-Bus connection cannot be succesfully established. - /// - public class ConnectException : Exception - { - /// - /// Creates an instance of the ConnectException with the specified message. - /// - /// The error message that explains the reason for the exception. - public ConnectException(string message) : base(message) - { } - - /// - /// Creates an instance of the ConnectException with the specified message and innerException. - /// - /// The error message that explains the reason for the exception.. - /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public ConnectException(string message, Exception innerException) : base(message, innerException) - { } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Connection2.cs b/src/Linux/Tmds.DBus/Connection2.cs deleted file mode 100644 index b67d897c5d..0000000000 --- a/src/Linux/Tmds.DBus/Connection2.cs +++ /dev/null @@ -1,1063 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; -using System.Net; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Tmds.DBus.CodeGen; -using Tmds.DBus.Protocol; - -namespace Tmds.DBus -{ - /// - /// Connection with a D-Bus peer. - /// - public class Connection2 : IConnection - { - [Flags] - private enum ConnectionType - { - None = 0, - ClientManual = 1, - ClientAutoConnect = 2, - Server = 4 - } - - /// - /// Assembly name where the dynamically generated code resides. - /// - public const string DynamicAssemblyName = "Tmds.DBus.Emit, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a8770f460cce31df0feb6f94b328aebd55bffeb5c69504593df097fdd9b29586dbd155419031834411c8919516cc565dee6b813c033676218496edcbe7939c0dd1f919f3d1a228ebe83b05a3bbdbae53ce11bcf4c04a42d8df1a83c2d06cb4ebb0b447e3963f48a1ca968996f3f0db8ab0e840a89d0a5d5a237e2f09189ed3"; - - private static Connection2 s_systemConnection2; - private static Connection2 s_sessionConnection2; - private static readonly object NoDispose = new object(); - - /// - /// An AutoConnect Connection to the system bus. - /// - public static Connection2 System => s_systemConnection2 ?? CreateSystemConnection(); - - /// - /// An AutoConnect Connection to the session bus. - /// - public static Connection2 Session => s_sessionConnection2 ?? CreateSessionConnection(); - - private class ProxyFactory : IProxyFactory - { - public Connection2 Connection2 { get; } - public ProxyFactory(Connection2 connection2) - { - Connection2 = connection2; - } - public T CreateProxy(string serviceName, ObjectPath2 path2) - { - return Connection2.CreateProxy(serviceName, path2); - } - } - - private readonly object _gate = new object(); - private readonly Dictionary _registeredObjects = new Dictionary(); - private readonly Func> _connectFunction; - private readonly Action _disposeAction; - private readonly SynchronizationContext _synchronizationContext; - private readonly bool _runContinuationsAsynchronously; - private readonly ConnectionType _connectionType; - - private ConnectionState _state = ConnectionState.Created; - private bool _disposed = false; - private IProxyFactory _factory; - private DBusConnection _dbusConnection; - private Task _dbusConnectionTask; - private TaskCompletionSource _dbusConnectionTcs; - private CancellationTokenSource _connectCts; - private Exception _disconnectReason; - private IDBus _bus; - private EventHandler _stateChangedEvent; - private object _disposeUserToken = NoDispose; - - private IDBus DBus - { - get - { - if (_bus != null) - { - return _bus; - } - lock (_gate) - { - _bus = _bus ?? CreateProxy(DBusConnection.DBusServiceName, DBusConnection.DBusObjectPath2); - return _bus; - } - } - } - - /// - /// Occurs when the state changes. - /// - /// - /// The event handler will be called when it is added to the event. - /// The event handler is invoked on the ConnectionOptions.SynchronizationContext. - /// - public event EventHandler StateChanged - { - add - { - lock (_gate) - { - _stateChangedEvent += value; - if (_state != ConnectionState.Created) - { - EmitConnectionStateChanged(value); - } - } - } - remove - { - lock (_gate) - { - _stateChangedEvent -= value; - } - } - } - - /// - /// Creates a new Connection with a specific address. - /// - /// Address of the D-Bus peer. - public Connection2(string address) : - this(new ClientConnectionOptions(address)) - { } - - /// - /// Creates a new Connection with specific ConnectionOptions. - /// - /// - public Connection2(ConnectionOptions connectionOptions) - { - if (connectionOptions == null) - throw new ArgumentNullException(nameof(connectionOptions)); - - _factory = new ProxyFactory(this); - _synchronizationContext = connectionOptions.SynchronizationContext; - if (connectionOptions is ClientConnectionOptions clientConnectionOptions) - { - _connectionType = clientConnectionOptions.AutoConnect ? ConnectionType.ClientAutoConnect : ConnectionType.ClientManual ; - _connectFunction = clientConnectionOptions.SetupAsync; - _disposeAction = clientConnectionOptions.Teardown; - _runContinuationsAsynchronously = clientConnectionOptions.RunContinuationsAsynchronously; - } - else if (connectionOptions is ServerConnectionOptions serverConnectionOptions) - { - _connectionType = ConnectionType.Server; - _state = ConnectionState.Connected; - _dbusConnection = new DBusConnection(localServer: true, runContinuationsAsynchronously: false); - _dbusConnectionTask = Task.FromResult(_dbusConnection); - serverConnectionOptions.Connection2 = this; - } - else - { - throw new NotSupportedException($"Unknown ConnectionOptions type: '{typeof(ConnectionOptions).FullName}'"); - } - } - - /// - /// Connect with the remote peer. - /// - /// - /// Information about the established connection. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - public async Task ConnectAsync() - => (await DoConnectAsync().ConfigureAwait(false)).ConnectionInfo; - - private async Task DoConnectAsync() - { - Task connectionTask = null; - bool alreadyConnecting = false; - lock (_gate) - { - if (_disposed) - { - ThrowDisposed(); - } - - if (_connectionType == ConnectionType.ClientManual) - { - if (_state != ConnectionState.Created) - { - throw new InvalidOperationException("Can only connect once"); - } - } - else - { - if (_state == ConnectionState.Connecting || _state == ConnectionState.Connected) - { - connectionTask = _dbusConnectionTask; - alreadyConnecting = true; - } - } - if (!alreadyConnecting) - { - _connectCts = new CancellationTokenSource(); - _dbusConnectionTcs = new TaskCompletionSource(); - _dbusConnectionTask = _dbusConnectionTcs.Task; - connectionTask = _dbusConnectionTask; - _state = ConnectionState.Connecting; - - EmitConnectionStateChanged(); - } - } - - if (alreadyConnecting) - { - return await connectionTask.ConfigureAwait(false); - } - - DBusConnection connection; - object disposeUserToken = NoDispose; - try - { - ClientSetupResult connectionContext = await _connectFunction().ConfigureAwait(false); - disposeUserToken = connectionContext.TeardownToken; - connection = await DBusConnection.ConnectAsync(connectionContext, _runContinuationsAsynchronously, OnDisconnect, _connectCts.Token).ConfigureAwait(false); - } - catch (ConnectException ce) - { - if (disposeUserToken != NoDispose) - { - _disposeAction?.Invoke(disposeUserToken); - } - Disconnect(dispose: false, exception: ce); - throw; - } - catch (Exception e) - { - if (disposeUserToken != NoDispose) - { - _disposeAction?.Invoke(disposeUserToken); - } - var ce = new ConnectException(e.Message, e); - Disconnect(dispose: false, exception: ce); - throw ce; - } - lock (_gate) - { - if (_state == ConnectionState.Connecting) - { - _disposeUserToken = disposeUserToken; - _dbusConnection = connection; - _connectCts.Dispose(); - _connectCts = null; - _state = ConnectionState.Connected; - _dbusConnectionTcs.SetResult(connection); - _dbusConnectionTcs = null; - - EmitConnectionStateChanged(); - } - else - { - connection.Dispose(); - if (disposeUserToken != NoDispose) - { - _disposeAction?.Invoke(disposeUserToken); - } - } - ThrowIfNotConnected(); - } - return connection; - } - - /// - /// Disposes the connection. - /// - public void Dispose() - { - Disconnect(dispose: true, exception: CreateDisposedException()); - } - - /// - /// Creates a proxy object that represents a remote D-Bus object. - /// - /// Interface of the D-Bus object. - /// Name of the service that exposes the object. - /// Object path of the object. - /// - /// Proxy object. - /// - public T CreateProxy(string serviceName, ObjectPath2 path2) - { - CheckNotConnectionType(ConnectionType.Server); - return (T)CreateProxy(typeof(T), serviceName, path2); - } - - /// - /// Releases a service name assigned to the connection. - /// - /// Name of the service. - /// - /// true when the name was assigned to this connection; false when the name was not assigned to this connection. - /// - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - /// Error returned by remote peer. - /// - /// This operation is not supported for AutoConnection connections. - /// - public async Task UnregisterServiceAsync(string serviceName) - { - CheckNotConnectionType(ConnectionType.ClientAutoConnect); - var connection = GetConnectedConnection(); - var reply = await connection.ReleaseNameAsync(serviceName).ConfigureAwait(false); - return reply == ReleaseNameReply.ReplyReleased; - } - - /// - /// Queues a service name registration for the connection. - /// - /// Name of the service. - /// Action invoked when the service name is assigned to the connection. - /// Action invoked when the service name is no longer assigned to the connection. - /// Options for the registration. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - /// Error returned by remote peer. - /// Unexpected reply. - /// - /// This operation is not supported for AutoConnection connections. - /// - public async Task QueueServiceRegistrationAsync(string serviceName, Action onAquired = null, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default) - { - CheckNotConnectionType(ConnectionType.ClientAutoConnect); - var connection = GetConnectedConnection(); - if (!options.HasFlag(ServiceRegistrationOptions.AllowReplacement) && (onLost != null)) - { - throw new ArgumentException($"{nameof(onLost)} can only be set when {nameof(ServiceRegistrationOptions.AllowReplacement)} is also set", nameof(onLost)); - } - - RequestNameOptions requestOptions = RequestNameOptions.None; - if (options.HasFlag(ServiceRegistrationOptions.ReplaceExisting)) - { - requestOptions |= RequestNameOptions.ReplaceExisting; - } - if (options.HasFlag(ServiceRegistrationOptions.AllowReplacement)) - { - requestOptions |= RequestNameOptions.AllowReplacement; - } - var reply = await connection.RequestNameAsync(serviceName, requestOptions, onAquired, onLost, CaptureSynchronizationContext()).ConfigureAwait(false); - switch (reply) - { - case RequestNameReply.PrimaryOwner: - case RequestNameReply.InQueue: - return; - case RequestNameReply.Exists: - case RequestNameReply.AlreadyOwner: - default: - throw new ProtocolException("Unexpected reply"); - } - } - - /// - /// Queues a service name registration for the connection. - /// - /// Name of the service. - /// Options for the registration. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - /// Error returned by remote peer. - /// Unexpected reply. - /// - /// This operation is not supported for AutoConnection connections. - /// - public Task QueueServiceRegistrationAsync(string serviceName, ServiceRegistrationOptions options) - => QueueServiceRegistrationAsync(serviceName, null, null, options); - - /// - /// Requests a service name to be assigned to the connection. - /// - /// Name of the service. - /// Action invoked when the service name is no longer assigned to the connection. - /// - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - /// Error returned by remote peer. - /// - /// This operation is not supported for AutoConnection connections. - /// - public async Task RegisterServiceAsync(string serviceName, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default) - { - CheckNotConnectionType(ConnectionType.ClientAutoConnect); - var connection = GetConnectedConnection(); - if (!options.HasFlag(ServiceRegistrationOptions.AllowReplacement) && (onLost != null)) - { - throw new ArgumentException($"{nameof(onLost)} can only be set when {nameof(ServiceRegistrationOptions.AllowReplacement)} is also set", nameof(onLost)); - } - - RequestNameOptions requestOptions = RequestNameOptions.DoNotQueue; - if (options.HasFlag(ServiceRegistrationOptions.ReplaceExisting)) - { - requestOptions |= RequestNameOptions.ReplaceExisting; - } - if (options.HasFlag(ServiceRegistrationOptions.AllowReplacement)) - { - requestOptions |= RequestNameOptions.AllowReplacement; - } - var reply = await connection.RequestNameAsync(serviceName, requestOptions, null, onLost, CaptureSynchronizationContext()).ConfigureAwait(false); - switch (reply) - { - case RequestNameReply.PrimaryOwner: - return; - case RequestNameReply.Exists: - throw new InvalidOperationException("Service is registered by another connection"); - case RequestNameReply.AlreadyOwner: - throw new InvalidOperationException("Service is already registered by this connection"); - case RequestNameReply.InQueue: - default: - throw new ProtocolException("Unexpected reply"); - } - } - - /// - /// Requests a service name to be assigned to the connection. - /// - /// Name of the service. - /// - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed after it was established. - /// Error returned by remote peer. - /// - /// This operation is not supported for AutoConnection connections. - /// - public Task RegisterServiceAsync(string serviceName, ServiceRegistrationOptions options) - => RegisterServiceAsync(serviceName, null, options); - - /// - /// Publishes an object. - /// - /// Object to publish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed. - public Task RegisterObjectAsync(IDBusObject o) - { - return RegisterObjectsAsync(new[] { o }); - } - - /// - /// Publishes objects. - /// - /// Objects to publish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection was closed. - /// - /// This operation is not supported for AutoConnection connections. - /// - public async Task RegisterObjectsAsync(IEnumerable objects) - { - CheckNotConnectionType(ConnectionType.ClientAutoConnect); - var connection = GetConnectedConnection(); - var assembly = DynamicAssembly.Instance; - var registrations = new List(); - foreach (var o in objects) - { - var implementationType = assembly.GetExportTypeInfo(o.GetType()); - var objectPath = o.ObjectPath2; - var registration = (DBusAdapter)Activator.CreateInstance(implementationType.AsType(), _dbusConnection, objectPath, o, _factory, CaptureSynchronizationContext()); - registrations.Add(registration); - } - - lock (_gate) - { - connection.AddMethodHandlers(registrations.Select(r => new KeyValuePair(r.Path2, r.HandleMethodCall))); - - foreach (var registration in registrations) - { - _registeredObjects.Add(registration.Path2, registration); - } - } - try - { - foreach (var registration in registrations) - { - await registration.WatchSignalsAsync().ConfigureAwait(false); - } - lock (_gate) - { - foreach (var registration in registrations) - { - registration.CompleteRegistration(); - } - } - } - catch - { - lock (_gate) - { - foreach (var registration in registrations) - { - registration.Unregister(); - _registeredObjects.Remove(registration.Path2); - } - connection.RemoveMethodHandlers(registrations.Select(r => r.Path2)); - } - throw; - } - } - - /// - /// Unpublishes an object. - /// - /// Path of object to unpublish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public void UnregisterObject(ObjectPath2 path2) - => UnregisterObjects(new[] { path2 }); - - /// - /// Unpublishes an object. - /// - /// object to unpublish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public void UnregisterObject(IDBusObject o) - => UnregisterObject(o.ObjectPath2); - - /// - /// Unpublishes objects. - /// - /// Paths of objects to unpublish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - /// - /// This operation is not supported for AutoConnection connections. - /// - public void UnregisterObjects(IEnumerable paths) - { - CheckNotConnectionType(ConnectionType.ClientAutoConnect); - lock (_gate) - { - var connection = GetConnectedConnection(); - - foreach(var objectPath in paths) - { - DBusAdapter registration; - if (_registeredObjects.TryGetValue(objectPath, out registration)) - { - registration.Unregister(); - _registeredObjects.Remove(objectPath); - } - } - - connection.RemoveMethodHandlers(paths); - } - } - - /// - /// Unpublishes objects. - /// - /// Objects to unpublish. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - /// - /// This operation is not supported for AutoConnection connections. - /// - public void UnregisterObjects(IEnumerable objects) - => UnregisterObjects(objects.Select(o => o.ObjectPath2)); - - /// - /// List services that can be activated. - /// - /// - /// List of activatable services. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public Task ListActivatableServicesAsync() - => DBus.ListActivatableNamesAsync(); - - /// - /// Resolves the local address for a service. - /// - /// Name of the service. - /// - /// Local address of service. null is returned when the service name is not available. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public async Task ResolveServiceOwnerAsync(string serviceName) - { - try - { - return await DBus.GetNameOwnerAsync(serviceName).ConfigureAwait(false); - } - catch (DBusException e) when (e.ErrorName == "org.freedesktop.DBus.Error.NameHasNoOwner") - { - return null; - } - catch - { - throw; - } - } - - /// - /// Activates a service. - /// - /// Name of the service. - /// - /// The result of the activation. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public Task ActivateServiceAsync(string serviceName) - => DBus.StartServiceByNameAsync(serviceName, 0); - - /// - /// Checks if a service is available. - /// - /// Name of the service. - /// - /// true when the service is available, false otherwise. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public Task IsServiceActiveAsync(string serviceName) - => DBus.NameHasOwnerAsync(serviceName); - - /// - /// Resolves the local address for a service. - /// - /// Name of the service. - /// Action invoked when the local name of the service changes. - /// Action invoked when the connection closes. - /// - /// Disposable that allows to stop receiving notifications. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - /// - /// The event handler will be called when the service name is already registered. - /// - public async Task ResolveServiceOwnerAsync(string serviceName, Action handler, Action onError = null) - { - if (serviceName == "*") - { - serviceName = ".*"; - } - - var synchronizationContext = CaptureSynchronizationContext(); - var wrappedDisposable = new WrappedDisposable(synchronizationContext); - bool namespaceLookup = serviceName.EndsWith(".*", StringComparison.Ordinal); - bool _eventEmitted = false; - var _gate = new object(); - var _emittedServices = namespaceLookup ? new List() : null; - - Action handleEvent = (ownerChange, ex) => { - if (ex != null) - { - if (onError == null) - { - return; - } - wrappedDisposable.Call(onError, ex, disposes: true); - return; - } - bool first = false; - lock (_gate) - { - if (namespaceLookup) - { - first = _emittedServices?.Contains(ownerChange.ServiceName) == false; - _emittedServices?.Add(ownerChange.ServiceName); - } - else - { - first = _eventEmitted == false; - _eventEmitted = true; - } - } - if (first) - { - if (ownerChange.NewOwner == null) - { - return; - } - ownerChange.OldOwner = null; - } - wrappedDisposable.Call(handler, ownerChange); - }; - - var connection = await GetConnectionTask().ConfigureAwait(false); - wrappedDisposable.Disposable = await connection.WatchNameOwnerChangedAsync(serviceName, handleEvent).ConfigureAwait(false); - if (namespaceLookup) - { - serviceName = serviceName.Substring(0, serviceName.Length - 2); - } - try - { - if (namespaceLookup) - { - var services = await ListServicesAsync().ConfigureAwait(false); - foreach (var service in services) - { - if (service.StartsWith(serviceName, StringComparison.Ordinal) - && ( (service.Length == serviceName.Length) - || (service[serviceName.Length] == '.') - || (serviceName.Length == 0 && service[0] != ':'))) - { - var currentName = await ResolveServiceOwnerAsync(service).ConfigureAwait(false); - lock (_gate) - { - if (currentName != null && !_emittedServices.Contains(serviceName)) - { - var e = new ServiceOwnerChangedEventArgs(service, null, currentName); - handleEvent(e, null); - } - } - } - } - lock (_gate) - { - _emittedServices = null; - } - } - else - { - var currentName = await ResolveServiceOwnerAsync(serviceName).ConfigureAwait(false); - lock (_gate) - { - if (currentName != null && !_eventEmitted) - { - var e = new ServiceOwnerChangedEventArgs(serviceName, null, currentName); - handleEvent(e, null); - } - } - } - return wrappedDisposable; - } - catch (Exception ex) - { - handleEvent(default(ServiceOwnerChangedEventArgs), ex); - } - - return wrappedDisposable; - } - - /// - /// List services that are available. - /// - /// - /// List of available services. - /// - /// There was an error establishing the connection. - /// The connection has been disposed. - /// The operation is invalid in the current state. - /// The connection is closed. - public Task ListServicesAsync() - => DBus.ListNamesAsync(); - - internal Task StartServerAsync(string address) - { - lock (_gate) - { - ThrowIfNotConnected(); - return _dbusConnection.StartServerAsync(address); - } - } - - // Used by tests - internal void Connect(DBusConnection dbusConnection) - { - lock (_gate) - { - if (_state != ConnectionState.Created) - { - throw new InvalidOperationException("Can only connect once"); - } - _dbusConnection = dbusConnection; - _dbusConnectionTask = Task.FromResult(_dbusConnection); - _state = ConnectionState.Connected; - } - } - - private object CreateProxy(Type interfaceType, string busName, ObjectPath2 path2) - { - var assembly = DynamicAssembly.Instance; - var implementationType = assembly.GetProxyTypeInfo(interfaceType); - - DBusObjectProxy instance = (DBusObjectProxy)Activator.CreateInstance(implementationType.AsType(), - new object[] { this, _factory, busName, path2 }); - - return instance; - } - - private void OnDisconnect(Exception e) - { - Disconnect(dispose: false, exception: e); - } - - private void ThrowIfNotConnected() - => ThrowIfNotConnected(_disposed, _state, _disconnectReason); - - internal static void ThrowIfNotConnected(bool disposed, ConnectionState state, Exception disconnectReason) - { - if (disposed) - { - ThrowDisposed(); - } - if (state == ConnectionState.Disconnected) - { - throw new DisconnectedException(disconnectReason); - } - else if (state == ConnectionState.Created) - { - throw new InvalidOperationException("Not Connected"); - } - else if (state == ConnectionState.Connecting) - { - throw new InvalidOperationException("Connecting"); - } - } - - internal static Exception CreateDisposedException() - => new ObjectDisposedException(typeof(Connection2).FullName); - - private static void ThrowDisposed() - { - throw CreateDisposedException(); - } - - internal static void ThrowIfNotConnecting(bool disposed, ConnectionState state, Exception disconnectReason) - { - if (disposed) - { - ThrowDisposed(); - } - if (state == ConnectionState.Disconnected) - { - throw new DisconnectedException(disconnectReason); - } - else if (state == ConnectionState.Created) - { - throw new InvalidOperationException("Not Connected"); - } - else if (state == ConnectionState.Connected) - { - throw new InvalidOperationException("Already Connected"); - } - } - - private Task GetConnectionTask() - { - var connectionTask = Volatile.Read(ref _dbusConnectionTask); - if (connectionTask != null) - { - return connectionTask; - } - if (_connectionType == ConnectionType.ClientAutoConnect) - { - return DoConnectAsync(); - } - else - { - return Task.FromResult(GetConnectedConnection()); - } - } - - private DBusConnection GetConnectedConnection() - { - var connection = Volatile.Read(ref _dbusConnection); - if (connection != null) - { - return connection; - } - lock (_gate) - { - ThrowIfNotConnected(); - return _dbusConnection; - } - } - - private void CheckNotConnectionType(ConnectionType disallowed) - { - if ((_connectionType & disallowed) != ConnectionType.None) - { - if (_connectionType == ConnectionType.ClientAutoConnect) - { - throw new InvalidOperationException($"Operation not supported for {nameof(ClientConnectionOptions.AutoConnect)} Connection."); - } - else if (_connectionType == ConnectionType.Server) - { - throw new InvalidOperationException($"Operation not supported for Server-based Connection."); - } - } - } - - private void Disconnect(bool dispose, Exception exception) - { - lock (_gate) - { - if (dispose) - { - _disposed = true; - } - var previousState = _state; - if (previousState == ConnectionState.Disconnecting || previousState == ConnectionState.Disconnected || previousState == ConnectionState.Created) - { - return; - } - - _disconnectReason = exception; - - var connection = _dbusConnection; - var connectionCts = _connectCts;; - var dbusConnectionTask = _dbusConnectionTask; - var dbusConnectionTcs = _dbusConnectionTcs; - var disposeUserToken = _disposeUserToken; - _dbusConnection = null; - _connectCts = null; - _dbusConnectionTask = null; - _dbusConnectionTcs = null; - _disposeUserToken = NoDispose; - - foreach (var registeredObject in _registeredObjects) - { - registeredObject.Value.Unregister(); - } - _registeredObjects.Clear(); - - _state = ConnectionState.Disconnecting; - EmitConnectionStateChanged(); - - connectionCts?.Cancel(); - connectionCts?.Dispose(); - dbusConnectionTcs?.SetException( - dispose ? CreateDisposedException() : - exception.GetType() == typeof(ConnectException) ? exception : - new DisconnectedException(exception)); - connection?.Disconnect(dispose, exception); - if (disposeUserToken != NoDispose) - { - _disposeAction?.Invoke(disposeUserToken); - } - - if (_state == ConnectionState.Disconnecting) - { - _state = ConnectionState.Disconnected; - EmitConnectionStateChanged(); - } - } - } - - private void EmitConnectionStateChanged(EventHandler handler = null) - { - var disconnectReason = _disconnectReason; - if (_state == ConnectionState.Connecting) - { - _disconnectReason = null; - } - - if (handler == null) - { - handler = _stateChangedEvent; - } - - if (handler == null) - { - return; - } - - if (disconnectReason != null - && disconnectReason.GetType() != typeof(ConnectException) - && disconnectReason.GetType() != typeof(ObjectDisposedException) - && disconnectReason.GetType() != typeof(DisconnectedException)) - { - disconnectReason = new DisconnectedException(disconnectReason); - } - var connectionInfo = _state == ConnectionState.Connected ? _dbusConnection.ConnectionInfo : null; - var stateChangeEvent = new ConnectionStateChangedEventArgs(_state, disconnectReason, connectionInfo); - - - if (_synchronizationContext != null && SynchronizationContext.Current != _synchronizationContext) - { - _synchronizationContext.Post(_ => handler(this, stateChangeEvent), null); - } - else - { - handler(this, stateChangeEvent); - } - } - - internal async Task CallMethodAsync(Message message) - { - var connection = await GetConnectionTask().ConfigureAwait(false); - try - { - return await connection.CallMethodAsync(message).ConfigureAwait(false); - } - catch (DisconnectedException) when (_connectionType == ConnectionType.ClientAutoConnect) - { - connection = await GetConnectionTask().ConfigureAwait(false); - return await connection.CallMethodAsync(message).ConfigureAwait(false); - } - } - - internal async Task WatchSignalAsync(ObjectPath2 path2, string @interface, string signalName, SignalHandler handler) - { - var connection = await GetConnectionTask().ConfigureAwait(false); - try - { - return await connection.WatchSignalAsync(path2, @interface, signalName, handler).ConfigureAwait(false); - } - catch (DisconnectedException) when (_connectionType == ConnectionType.ClientAutoConnect) - { - connection = await GetConnectionTask().ConfigureAwait(false); - return await connection.WatchSignalAsync(path2, @interface, signalName, handler).ConfigureAwait(false); - } - } - - internal SynchronizationContext CaptureSynchronizationContext() => _synchronizationContext; - - private static Connection2 CreateSessionConnection() => CreateConnection(Address.Session, ref s_sessionConnection2); - - private static Connection2 CreateSystemConnection() => CreateConnection(Address.System, ref s_systemConnection2); - - private static Connection2 CreateConnection(string address, ref Connection2 connection2) - { - address = address ?? "unix:"; - if (Volatile.Read(ref connection2) != null) - { - return connection2; - } - var newConnection = new Connection2(new ClientConnectionOptions(address) { AutoConnect = true, SynchronizationContext = null }); - Interlocked.CompareExchange(ref connection2, newConnection, null); - return connection2; - } - } -} diff --git a/src/Linux/Tmds.DBus/ConnectionInfo.cs b/src/Linux/Tmds.DBus/ConnectionInfo.cs deleted file mode 100644 index 083aa79ced..0000000000 --- a/src/Linux/Tmds.DBus/ConnectionInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - /// - /// Information about established Connection. - /// - public class ConnectionInfo - { - /// - /// Creates an instance of ConnectionInfo. - /// - /// Name assigned by the bus to the connection. - public ConnectionInfo(string localName) - { - LocalName = localName; - } - - /// - /// Local name assigned by the bus to the connection. - /// - public string LocalName { get; } - - /// - /// Returns whether the remote peer is a bus. - /// - public bool RemoteIsBus => !string.IsNullOrEmpty(LocalName); - } -} diff --git a/src/Linux/Tmds.DBus/ConnectionOptions.cs b/src/Linux/Tmds.DBus/ConnectionOptions.cs deleted file mode 100644 index 8945437a45..0000000000 --- a/src/Linux/Tmds.DBus/ConnectionOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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 -{ - /// - /// Options that configure the behavior of a Connection. - /// - public abstract class ConnectionOptions - { - internal ConnectionOptions() - {} - - /// - /// SynchronizationContext used for event handlers and callbacks. - /// - public SynchronizationContext SynchronizationContext { get; set; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/ConnectionState.cs b/src/Linux/Tmds.DBus/ConnectionState.cs deleted file mode 100644 index 5a935f2660..0000000000 --- a/src/Linux/Tmds.DBus/ConnectionState.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - /// - /// State of the Connection. - /// - public enum ConnectionState - { - /// No connection attempt has been made. - Created, - /// Connecting to remote peer. - Connecting, - /// Connection established. - Connected, - /// Connection is closing. - Disconnecting, - /// Connection is closed. - Disconnected - } -} diff --git a/src/Linux/Tmds.DBus/ConnectionStateChangedEventArgs.cs b/src/Linux/Tmds.DBus/ConnectionStateChangedEventArgs.cs deleted file mode 100644 index 919fddd014..0000000000 --- a/src/Linux/Tmds.DBus/ConnectionStateChangedEventArgs.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Event data for the Connection StateChanged event. - /// - public struct ConnectionStateChangedEventArgs - { - /// - /// Creates an instance of ConnectionStateChangedEventArgs. - /// - /// State of the connection. - /// Reason the connection closed. - /// Information about established connection. - public ConnectionStateChangedEventArgs(ConnectionState state, Exception disconnectReason, ConnectionInfo connectionInfo) - { - State = state; - DisconnectReason = disconnectReason; - ConnectionInfo = connectionInfo; - } - - /// - /// ConnectionInfo for established connection. - /// - /// - /// This property is set for the Connected event. - /// - public ConnectionInfo ConnectionInfo { get; } - - /// - /// New connection state. - /// - public ConnectionState State { get; } - - /// - /// Reason the connection closed. - /// - /// - /// This property is set for the Disconnecting, Disconnected and following Connecting event. - /// - public Exception DisconnectReason { get; } - } -} diff --git a/src/Linux/Tmds.DBus/DBusConnection.cs b/src/Linux/Tmds.DBus/DBusConnection.cs deleted file mode 100644 index 32a4867fbf..0000000000 --- a/src/Linux/Tmds.DBus/DBusConnection.cs +++ /dev/null @@ -1,1059 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Tmds.DBus.Protocol; -using Tmds.DBus.Transports; -using System.Threading.Tasks.Sources; -namespace Tmds.DBus -{ - class DBusConnection - { - class MyValueTaskSource : IValueTaskSource - { - private ManualResetValueTaskSourceCore _core; - private volatile bool _continuationSet; - - public MyValueTaskSource(bool runContinuationsAsynchronously) - { - RunContinuationsAsynchronously = runContinuationsAsynchronously; - } - - public bool RunContinuationsAsynchronously - { - get => _core.RunContinuationsAsynchronously; - set => _core.RunContinuationsAsynchronously = value; - } - - public void SetResult(T result) - { - // Ensure we complete the Task from the read loop. - if (!RunContinuationsAsynchronously) - { - SpinWait wait = default; - while (!_continuationSet) - { - wait.SpinOnce(); - } - } - _core.SetResult(result); - } - - public void SetException(Exception exception) => _core.SetException(exception); - - public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token); - - public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) - { - _core.OnCompleted(continuation, state, token, flags); - _continuationSet = true; - } - - T IValueTaskSource.GetResult(short token) => _core.GetResult(token); - } - - private class SignalHandlerRegistration : IDisposable - { - public SignalHandlerRegistration(DBusConnection dbusConnection, SignalMatchRule rule, SignalHandler handler) - { - _connection = dbusConnection; - _rule = rule; - _handler = handler; - } - - public void Dispose() - { - _connection.RemoveSignalHandler(_rule, _handler); - } - - private DBusConnection _connection; - private SignalMatchRule _rule; - private SignalHandler _handler; - } - - private class NameOwnerWatcherRegistration : IDisposable - { - public NameOwnerWatcherRegistration(DBusConnection dbusConnection, string key, OwnerChangedMatchRule rule, Action handler) - { - _connection = dbusConnection; - _rule = rule; - _handler = handler; - _key = key; - } - - public void Dispose() - { - _connection.RemoveNameOwnerWatcher(_key, _rule, _handler); - } - - private DBusConnection _connection; - private OwnerChangedMatchRule _rule; - private Action _handler; - private string _key; - } - - private class ServiceNameRegistration - { - public Action OnAquire; - public Action OnLost; - public SynchronizationContext SynchronizationContext; - } - - public static readonly ObjectPath2 DBusObjectPath2 = new ObjectPath2("/org/freedesktop/DBus"); - public const string DBusServiceName = "org.freedesktop.DBus"; - public const string DBusInterface = "org.freedesktop.DBus"; - private static readonly char[] s_dot = new[] { '.' }; - - public static async Task ConnectAsync(ClientSetupResult connectionContext, bool runContinuationsAsynchronously, Action onDisconnect, CancellationToken cancellationToken) - { - var _entries = AddressEntry.ParseEntries(connectionContext.ConnectionAddress); - if (_entries.Length == 0) - { - throw new ArgumentException("No addresses were found", nameof(connectionContext.ConnectionAddress)); - } - - Guid _serverId = Guid.Empty; - IMessageStream stream = null; - var index = 0; - while (index < _entries.Length) - { - AddressEntry entry = _entries[index++]; - - _serverId = entry.Guid; - try - { - stream = await Transport.ConnectAsync(entry, connectionContext, cancellationToken).ConfigureAwait(false); - } - catch - { - if (index < _entries.Length) - continue; - throw; - } - - break; - } - - return await DBusConnection.CreateAndConnectAsync(stream, runContinuationsAsynchronously, onDisconnect).ConfigureAwait(false); - } - - private readonly IMessageStream _stream; - private readonly object _gate = new object(); - private Dictionary _signalHandlers = new Dictionary(); - private Dictionary> _nameOwnerWatchers = new Dictionary>(); - private Dictionary> _pendingMethods = new Dictionary>(); - private readonly Dictionary _methodHandlers = new Dictionary(); - private readonly Dictionary _childNames = new Dictionary(); - private readonly Dictionary _serviceNameRegistrations = new Dictionary(); - private readonly bool _runContinuationsAsynchronously; - - private ConnectionState _state = ConnectionState.Created; - private bool _disposed = false; - private Action _onDisconnect; - private Exception _disconnectReason; - private int _methodSerial; - - public ConnectionInfo ConnectionInfo { get; private set; } - - // For testing - internal static async Task CreateAndConnectAsync(IMessageStream stream, bool runContinuationsAsynchronously = false, Action onDisconnect = null) - { - var connection = new DBusConnection(stream, runContinuationsAsynchronously); - await connection.ConnectAsync(onDisconnect).ConfigureAwait(false); - return connection; - } - - private DBusConnection(IMessageStream stream, bool runContinuationsAsynchronously) - { - _stream = stream; - _runContinuationsAsynchronously = runContinuationsAsynchronously; - } - - public DBusConnection(bool localServer, bool runContinuationsAsynchronously) - { - if (localServer != true) - { - throw new ArgumentException("Constructor for LocalServer.", nameof(localServer)); - } - _stream = new LocalServer(this); - _runContinuationsAsynchronously = runContinuationsAsynchronously; - ConnectionInfo = new ConnectionInfo(string.Empty); - } - - private async Task ConnectAsync(Action onDisconnect) - { - lock (_gate) - { - if (_state != ConnectionState.Created) - { - throw new InvalidOperationException("Unable to connect"); - } - _state = ConnectionState.Connecting; - } - - if (SynchronizationContext.Current != null) - { - SynchronizationContext.SetSynchronizationContext(null); - await Task.Yield(); - } - - _onDisconnect = OnDisconnect; - - ReceiveMessages(_stream, EmitDisconnected); - - string localName = await CallHelloAsync().ConfigureAwait(false); - ConnectionInfo = new ConnectionInfo(localName); - - lock (_gate) - { - if (_state == ConnectionState.Connecting) - { - _state = ConnectionState.Connected; - } - ThrowIfNotConnected(); - - _onDisconnect = onDisconnect; - } - } - - private void OnDisconnect(Exception e) - { - Disconnect(dispose: false, exception: e); - } - - public Task CallMethodAsync(Message msg) - { - return CallMethodAsync(msg, checkConnected: true, checkReplyType: true); - } - - public void EmitSignal(Message message) - { - message.Header.Serial = GenerateSerial(); - _stream.TrySendMessage(message); - } - - public void AddMethodHandlers(IEnumerable> handlers) - { - lock (_gate) - { - foreach (var handler in handlers) - { - _methodHandlers.Add(handler.Key, handler.Value); - - AddChildName(handler.Key, checkChildNames: false); - } - } - } - - private void AddChildName(ObjectPath2 path2, bool checkChildNames) - { - if (path2 == ObjectPath2.Root) - { - return; - } - var parent = path2.Parent; - var decomposed = path2.Decomposed; - var name = decomposed[decomposed.Length - 1]; - string[] childNames = null; - if (_childNames.TryGetValue(parent, out childNames) && checkChildNames) - { - for (var i = 0; i < childNames.Length; i++) - { - if (childNames[i] == name) - { - return; - } - } - } - var newChildNames = new string[(childNames?.Length ?? 0) + 1]; - if (childNames != null) - { - for (var i = 0; i < childNames.Length; i++) - { - newChildNames[i] = childNames[i]; - } - } - newChildNames[newChildNames.Length - 1] = name; - _childNames[parent] = newChildNames; - - AddChildName(parent, checkChildNames: true); - } - - public void RemoveMethodHandlers(IEnumerable paths) - { - lock (_gate) - { - foreach (var path in paths) - { - var removed = _methodHandlers.Remove(path); - var hasChildren = _childNames.ContainsKey(path); - if (removed && !hasChildren) - { - RemoveChildName(path); - } - } - } - } - - private void RemoveChildName(ObjectPath2 path2) - { - if (path2 == ObjectPath2.Root) - { - return; - } - var parent = path2.Parent; - var decomposed = path2.Decomposed; - var name = decomposed[decomposed.Length - 1]; - string[] childNames = _childNames[parent]; - if (childNames.Length == 1) - { - _childNames.Remove(parent); - if (!_methodHandlers.ContainsKey(parent)) - { - RemoveChildName(parent); - } - } - else - { - int writeAt = 0; - bool found = false; - var newChildNames = new string[childNames.Length - 1]; - for (int i = 0; i < childNames.Length; i++) - { - if (!found && childNames[i] == name) - { - found = true; - } - else - { - newChildNames[writeAt++] = childNames[i]; - } - } - _childNames[parent] = newChildNames; - } - } - - public async Task WatchSignalAsync(ObjectPath2 path2, string @interface, string signalName, SignalHandler handler) - { - SignalMatchRule rule = new SignalMatchRule() - { - Interface = @interface, - Member = signalName, - Path = path2 - }; - - Task task = null; - lock (_gate) - { - ThrowIfNotConnected(); - if (_signalHandlers.ContainsKey(rule)) - { - _signalHandlers[rule] = (SignalHandler)Delegate.Combine(_signalHandlers[rule], handler); - task = Task.CompletedTask; - } - else - { - _signalHandlers[rule] = handler; - if (ConnectionInfo.RemoteIsBus) - { - task = CallAddMatchRuleAsync(rule.ToString()); - } - } - } - SignalHandlerRegistration registration = new SignalHandlerRegistration(this, rule, handler); - try - { - if (task != null) - { - await task.ConfigureAwait(false); - } - } - catch - { - registration.Dispose(); - throw; - } - return registration; - } - - public async Task RequestNameAsync(string name, RequestNameOptions options, Action onAquired, Action onLost, SynchronizationContext synchronzationContext) - { - lock (_gate) - { - ThrowIfNotConnected(); - ThrowIfRemoteIsNotBus(); - - if (_serviceNameRegistrations.ContainsKey(name)) - { - throw new InvalidOperationException("The name is already requested"); - } - _serviceNameRegistrations[name] = new ServiceNameRegistration - { - OnAquire = onAquired, - OnLost = onLost, - SynchronizationContext = synchronzationContext - }; - } - try - { - var reply = await CallRequestNameAsync(name, options).ConfigureAwait(false); - return reply; - } - catch - { - lock (_gate) - { - _serviceNameRegistrations.Remove(name); - } - throw; - } - } - - public Task ReleaseNameAsync(string name) - { - lock (_gate) - { - ThrowIfRemoteIsNotBus(); - - if (!_serviceNameRegistrations.ContainsKey(name)) - { - return Task.FromResult(ReleaseNameReply.NotOwner); - } - _serviceNameRegistrations.Remove(name); - - ThrowIfNotConnected(); - } - return CallReleaseNameAsync(name); - } - - public async Task WatchNameOwnerChangedAsync(string serviceName, Action handler) - { - var rule = new OwnerChangedMatchRule(serviceName); - string key = serviceName; - - Task task = null; - lock (_gate) - { - ThrowIfNotConnected(); - ThrowIfRemoteIsNotBus(); - - if (_nameOwnerWatchers.ContainsKey(key)) - { - _nameOwnerWatchers[key] = (Action)Delegate.Combine(_nameOwnerWatchers[key], handler); - task = Task.CompletedTask; - } - else - { - _nameOwnerWatchers[key] = handler; - task = CallAddMatchRuleAsync(rule.ToString()); - } - } - NameOwnerWatcherRegistration registration = new NameOwnerWatcherRegistration(this, key, rule, handler); - try - { - await task.ConfigureAwait(false); - } - catch - { - registration.Dispose(); - throw; - } - return registration; - } - - private void ThrowIfRemoteIsNotBus() - { - if (ConnectionInfo.RemoteIsBus != true) - { - throw new InvalidOperationException("The remote peer is not a bus"); - } - } - - internal async void ReceiveMessages(IMessageStream peer, Action disconnectAction) - { - try - { - while (true) - { - Message msg = await peer.ReceiveMessageAsync().ConfigureAwait(false); - if (msg == null) - { - throw new IOException("Connection closed by peer"); - } - HandleMessage(msg, peer); - } - } - catch (Exception e) - { - disconnectAction?.Invoke(peer, e); - } - } - - private void EmitDisconnected(IMessageStream peer, Exception e) - { - lock (_gate) - { - _onDisconnect?.Invoke(e); - _onDisconnect = null; - } - } - - private void HandleMessage(Message msg, IMessageStream peer) - { - uint? serial = msg.Header.ReplySerial; - if (serial != null) - { - uint serialValue = (uint)serial; - TaskCompletionSource pending = null; - lock (_gate) - { - if (_pendingMethods?.TryGetValue(serialValue, out pending) == true) - { - _pendingMethods.Remove(serialValue); - } - } - if (pending != null) - { - pending.SetResult(msg); - } - else - { - throw new ProtocolException("Unexpected reply message received: MessageType = '" + msg.Header.MessageType + "', ReplySerial = " + serialValue); - } - return; - } - - switch (msg.Header.MessageType) - { - case MessageType.MethodCall: - HandleMethodCall(msg, peer); - break; - case MessageType.Signal: - HandleSignal(msg); - break; - case MessageType.Error: - string errMsg = String.Empty; - if (msg.Header.Signature.Value.Value.StartsWith("s", StringComparison.Ordinal)) - { - MessageReader reader = new MessageReader(msg, null); - errMsg = reader.ReadString(); - } - throw new DBusException(msg.Header.ErrorName, errMsg); - case MessageType.Invalid: - default: - throw new ProtocolException("Invalid message received: MessageType='" + msg.Header.MessageType + "'"); - } - } - - private void HandleSignal(Message msg) - { - switch (msg.Header.Interface) - { - case "org.freedesktop.DBus": - switch (msg.Header.Member) - { - case "NameAcquired": - case "NameLost": - { - MessageReader reader = new MessageReader(msg, null); - var name = reader.ReadString(); - bool aquiredNotLost = msg.Header.Member == "NameAcquired"; - OnNameAcquiredOrLost(name, aquiredNotLost); - return; - } - case "NameOwnerChanged": - { - MessageReader reader = new MessageReader(msg, null); - var serviceName = reader.ReadString(); - if (serviceName[0] == ':') - { - return; - } - var oldOwner = reader.ReadString(); - oldOwner = string.IsNullOrEmpty(oldOwner) ? null : oldOwner; - var newOwner = reader.ReadString(); - newOwner = string.IsNullOrEmpty(newOwner) ? null : newOwner; - Action watchers = null; - var splitName = serviceName.Split(s_dot); - var keys = new string[splitName.Length + 2]; - keys[0] = ".*"; - var sb = new StringBuilder(); - for (int i = 0; i < splitName.Length; i++) - { - sb.Append(splitName[i]); - sb.Append(".*"); - keys[i + 1] = sb.ToString(); - sb.Remove(sb.Length - 1, 1); - } - keys[keys.Length - 1] = serviceName; - lock (_gate) - { - foreach (var key in keys) - { - Action keyWatchers = null; - if (_nameOwnerWatchers?.TryGetValue(key, out keyWatchers) == true) - { - watchers += keyWatchers; - } - } - } - watchers?.Invoke(new ServiceOwnerChangedEventArgs(serviceName, oldOwner, newOwner), null); - return; - } - default: - break; - } - break; - default: - break; - } - - SignalMatchRule rule = new SignalMatchRule() - { - Interface = msg.Header.Interface, - Member = msg.Header.Member, - Path = msg.Header.Path.Value - }; - - SignalHandler signalHandler = null; - lock (_gate) - { - if (_signalHandlers?.TryGetValue(rule, out signalHandler) == true) - { - try - { - signalHandler(msg, null); - } - catch (Exception e) - { - throw new InvalidOperationException("Signal handler for " + msg.Header.Interface + "." + msg.Header.Member + " threw an exception", e); - } - } - } - } - - private void OnNameAcquiredOrLost(string name, bool aquiredNotLost) - { - Action action = null; - SynchronizationContext synchronizationContext = null; - lock (_gate) - { - ServiceNameRegistration registration; - if (_serviceNameRegistrations.TryGetValue(name, out registration)) - { - action = aquiredNotLost ? registration.OnAquire : registration.OnLost; - synchronizationContext = registration.SynchronizationContext; - } - } - if (action != null) - { - if (synchronizationContext != null) - { - synchronizationContext.Post(_ => action(), null); - } - else - { - action(); - } - } - } - - public void Dispose() - { - Disconnect(dispose: true, exception: null); - } - - public void Disconnect(bool dispose, Exception exception) - { - Dictionary> pendingMethods = null; - Dictionary signalHandlers = null; - Dictionary> nameOwnerWatchers = null; - lock (_gate) - { - if (_state == ConnectionState.Disconnected || _state == ConnectionState.Created) - { - return; - } - - _state = ConnectionState.Disconnected; - _disposed = dispose; - _stream.Dispose(); - _disconnectReason = exception; - pendingMethods = _pendingMethods; - _pendingMethods = null; - signalHandlers = _signalHandlers; - _signalHandlers = null; - nameOwnerWatchers = _nameOwnerWatchers; - _nameOwnerWatchers = null; - _serviceNameRegistrations.Clear(); - - _onDisconnect = null; - - Func createException = () => - dispose ? Connection2.CreateDisposedException() : new DisconnectedException(exception); - - foreach (var watcher in nameOwnerWatchers) - { - watcher.Value(default(ServiceOwnerChangedEventArgs), createException()); - } - - foreach (var handler in signalHandlers) - { - handler.Value(null, createException()); - } - - foreach (var tcs in pendingMethods.Values) - { - tcs.SetException(createException()); - } - } - - if (_onDisconnect != null) - { - _onDisconnect(dispose ? null : exception); - } - } - - private void SendMessage(Message message, IMessageStream peer) - { - if (message.Header.Serial == 0) - { - message.Header.Serial = GenerateSerial(); - } - peer.TrySendMessage(message); - } - - private async void HandleMethodCall(Message methodCall, IMessageStream peer) - { - switch (methodCall.Header.Interface) - { - case "org.freedesktop.DBus.Peer": - switch (methodCall.Header.Member) - { - case "Ping": - { - SendMessage(MessageHelper.ConstructReply(methodCall), peer); - return; - } - case "GetMachineId": - { - SendMessage(MessageHelper.ConstructReply(methodCall, Environment.MachineId), peer); - return; - } - } - break; - } - - MethodHandler methodHandler; - if (_methodHandlers.TryGetValue(methodCall.Header.Path.Value, out methodHandler)) - { - var reply = await methodHandler(methodCall).ConfigureAwait(false); - if (methodCall.Header.ReplyExpected) - { - reply.Header.ReplySerial = methodCall.Header.Serial; - reply.Header.Destination = methodCall.Header.Sender; - SendMessage(reply, peer); - } - } - else - { - if (methodCall.Header.Interface == "org.freedesktop.DBus.Introspectable" - && methodCall.Header.Member == "Introspect" - && methodCall.Header.Path.HasValue) - { - var path = methodCall.Header.Path.Value; - var childNames = GetChildNames(path); - if (childNames.Length > 0) - { - var writer = new IntrospectionWriter(); - - writer.WriteDocType(); - writer.WriteNodeStart(path.Value); - writer.WriteIntrospectableInterface(); - writer.WritePeerInterface(); - foreach (var child in childNames) - { - writer.WriteChildNode(child); - } - writer.WriteNodeEnd(); - - var xml = writer.ToString(); - SendMessage(MessageHelper.ConstructReply(methodCall, xml), peer); - return; - } - } - SendUnknownMethodError(methodCall, peer); - } - } - - private async Task CallHelloAsync() - { - Message callMsg = new Message( - new Header(MessageType.MethodCall) - { - Path = DBusObjectPath2, - Interface = DBusInterface, - Member = "Hello", - Destination = DBusServiceName - }, - body: null, - unixFds: null - ); - - Message reply = await CallMethodAsync(callMsg, checkConnected: false, checkReplyType: false).ConfigureAwait(false); - - if (reply.Header.MessageType == MessageType.Error) - { - return string.Empty; - } - else if (reply.Header.MessageType == MessageType.MethodReturn) - { - var reader = new MessageReader(reply, null); - return reader.ReadString(); - } - else - { - throw new ProtocolException("Got unexpected message of type " + reply.Header.MessageType + " while waiting for a MethodReturn or Error"); - } - } - - private async Task CallRequestNameAsync(string name, RequestNameOptions options) - { - var writer = new MessageWriter(); - writer.WriteString(name); - writer.WriteUInt32((uint)options); - - Message callMsg = new Message( - new Header(MessageType.MethodCall) - { - Path = DBusObjectPath2, - Interface = DBusInterface, - Member = "RequestName", - Destination = DBusServiceName, - Signature = "su" - }, - writer.ToArray(), - writer.UnixFds - ); - - Message reply = await CallMethodAsync(callMsg, checkConnected: true, checkReplyType: true).ConfigureAwait(false); - - var reader = new MessageReader(reply, null); - var rv = reader.ReadUInt32(); - return (RequestNameReply)rv; - } - - private async Task CallReleaseNameAsync(string name) - { - var writer = new MessageWriter(); - writer.WriteString(name); - - Message callMsg = new Message( - new Header(MessageType.MethodCall) - { - Path = DBusObjectPath2, - Interface = DBusInterface, - Member = "ReleaseName", - Destination = DBusServiceName, - Signature = Signature.StringSig - }, - writer.ToArray(), - writer.UnixFds - ); - - Message reply = await CallMethodAsync(callMsg, checkConnected: true, checkReplyType: true).ConfigureAwait(false); - - var reader = new MessageReader(reply, null); - var rv = reader.ReadUInt32(); - return (ReleaseNameReply)rv; - } - - private void SendUnknownMethodError(Message callMessage, IMessageStream peer) - { - if (!callMessage.Header.ReplyExpected) - { - return; - } - - string errMsg = String.Format("Method \"{0}\" with signature \"{1}\" on interface \"{2}\" doesn't exist", - callMessage.Header.Member, - callMessage.Header.Signature?.Value, - callMessage.Header.Interface); - - SendErrorReply(callMessage, "org.freedesktop.DBus.Error.UnknownMethod", errMsg, peer); - } - - private void SendErrorReply(Message incoming, string errorName, string errorMessage, IMessageStream peer) - { - SendMessage(MessageHelper.ConstructErrorReply(incoming, errorName, errorMessage), peer); - } - - private uint GenerateSerial() - { - return (uint)Interlocked.Increment(ref _methodSerial); - } - - private async Task CallMethodAsync(Message msg, bool checkConnected, bool checkReplyType) - { - msg.Header.ReplyExpected = true; - var serial = GenerateSerial(); - msg.Header.Serial = serial; - - TaskCompletionSource pending = new TaskCompletionSource(_runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : default); - lock (_gate) - { - if (checkConnected) - { - ThrowIfNotConnected(); - } - else - { - ThrowIfNotConnecting(); - } - _pendingMethods[msg.Header.Serial] = pending; - } - - try - { - await _stream.SendMessageAsync(msg).ConfigureAwait(false); - } - catch - { - lock (_gate) - { - _pendingMethods?.Remove(serial); - } - throw; - } - - var reply = await pending.Task.ConfigureAwait(false); - - if (checkReplyType) - { - switch (reply.Header.MessageType) - { - case MessageType.MethodReturn: - return reply; - case MessageType.Error: - string errorMessage = String.Empty; - if (reply.Header.Signature?.Value?.StartsWith("s", StringComparison.Ordinal) == true) - { - MessageReader reader = new MessageReader(reply, null); - errorMessage = reader.ReadString(); - } - throw new DBusException(reply.Header.ErrorName, errorMessage); - default: - throw new ProtocolException("Got unexpected message of type " + reply.Header.MessageType + " while waiting for a MethodReturn or Error"); - } - } - - return reply; - } - - private void RemoveSignalHandler(SignalMatchRule rule, SignalHandler dlg) - { - lock (_gate) - { - if (_signalHandlers?.ContainsKey(rule) == true) - { - _signalHandlers[rule] = (SignalHandler)Delegate.Remove(_signalHandlers[rule], dlg); - if (_signalHandlers[rule] == null) - { - _signalHandlers.Remove(rule); - if (ConnectionInfo.RemoteIsBus) - { - CallRemoveMatchRule(rule.ToString()); - } - } - } - } - } - - private void RemoveNameOwnerWatcher(string key, OwnerChangedMatchRule rule, Action dlg) - { - lock (_gate) - { - if (_nameOwnerWatchers?.ContainsKey(key) == true) - { - _nameOwnerWatchers[key] = (Action)Delegate.Remove(_nameOwnerWatchers[key], dlg); - if (_nameOwnerWatchers[key] == null) - { - _nameOwnerWatchers.Remove(key); - CallRemoveMatchRule(rule.ToString()); - } - } - } - } - - private void CallRemoveMatchRule(string rule) - { - var reply = CallMethodAsync(DBusServiceName, DBusObjectPath2, DBusInterface, "RemoveMatch", rule); - } - - private Task CallAddMatchRuleAsync(string rule) - { - return CallMethodAsync(DBusServiceName, DBusObjectPath2, DBusInterface, "AddMatch", rule); - } - - private Task CallMethodAsync(string destination, ObjectPath2 objectPath2, string @interface, string method, string arg) - { - var header = new Header(MessageType.MethodCall) - { - Path = objectPath2, - Interface = @interface, - Member = @method, - Signature = Signature.StringSig, - Destination = destination - }; - var writer = new MessageWriter(); - writer.WriteString(arg); - var message = new Message( - header, - writer.ToArray(), - writer.UnixFds - ); - return CallMethodAsync(message); - } - - private void ThrowIfNotConnected() - => Connection2.ThrowIfNotConnected(_disposed, _state, _disconnectReason); - - private void ThrowIfNotConnecting() - => Connection2.ThrowIfNotConnecting(_disposed, _state, _disconnectReason); - - public string[] GetChildNames(ObjectPath2 path2) - { - lock (_gate) - { - string[] childNames = null; - if (_childNames.TryGetValue(path2, out childNames)) - { - return childNames; - } - else - { - return Array.Empty(); - } - } - } - - public Task StartServerAsync(string address) - { - var localServer = _stream as LocalServer; - if (localServer == null) - { - throw new InvalidOperationException("Not a server connection."); - } - return localServer.StartAsync(address); - } - } -} diff --git a/src/Linux/Tmds.DBus/DBusException.cs b/src/Linux/Tmds.DBus/DBusException.cs deleted file mode 100644 index 31983db524..0000000000 --- a/src/Linux/Tmds.DBus/DBusException.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Represents the D-Bus error message which is used to signal the unsuccesfull invocation of a method. - /// - public class DBusException : Exception - { - /// - /// Creates a new DBusException with the given name and message. - /// - /// Name of the error - /// Message of the error - public DBusException(string errorName, string errorMessage) : - base($"{errorName}: {errorMessage}") - { - ErrorName = errorName; - ErrorMessage = errorMessage; - } - - /// - /// Name of the error. - /// - public string ErrorName { get; } - - /// - /// Message of the error. - /// - public string ErrorMessage { get; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/DBusInterfaceAttribute.cs b/src/Linux/Tmds.DBus/DBusInterfaceAttribute.cs deleted file mode 100644 index 3cc61788b7..0000000000 --- a/src/Linux/Tmds.DBus/DBusInterfaceAttribute.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Provides information for mapping the C# interface to a D-Bus interface. - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class DBusInterfaceAttribute : Attribute - { - /// - /// Name of the D-Bus interface. - /// - public string Name { get; } - - /// - /// Method name of the property get method. Defaults to GetAsync. - /// - public string GetPropertyMethod { get; set; } - - /// - /// Method name of the property get method. Defaults to SetAsync. - /// - public string SetPropertyMethod { get; set; } - - /// - /// Method name of the property get all method. Defaults to GetAllAsync. - /// - public string GetAllPropertiesMethod { get; set; } - - /// - /// Method name of the property get all method. Defaults to WatchPropertiesAsync. - /// - public string WatchPropertiesMethod { get; set; } - - /// - /// Set to a type decorated with the Dictionary attribute used to provide property introspection information. When unset the type returned by the GetAllPropertiesMethod is used. - /// - public Type PropertyType { get; set; } - - /// - /// Creates a DBusInterfaceAttribute with the specified D-Bus interface name. - /// - /// D-Bus interface name - public DBusInterfaceAttribute(string name) - { - Name = name; - GetAllPropertiesMethod = "GetAllAsync"; - SetPropertyMethod = "SetAsync"; - GetPropertyMethod = "GetAsync"; - WatchPropertiesMethod = "WatchPropertiesAsync"; - PropertyType = null; - } - } -} diff --git a/src/Linux/Tmds.DBus/DictionaryAttribute.cs b/src/Linux/Tmds.DBus/DictionaryAttribute.cs deleted file mode 100644 index 6613be94bc..0000000000 --- a/src/Linux/Tmds.DBus/DictionaryAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Indicates the type must be marshalled as a D-Bus dictionary of a{sv}. - /// - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class DictionaryAttribute : Attribute - {} -} diff --git a/src/Linux/Tmds.DBus/DisconnectedException.cs b/src/Linux/Tmds.DBus/DisconnectedException.cs deleted file mode 100644 index 80731af20f..0000000000 --- a/src/Linux/Tmds.DBus/DisconnectedException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// 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. - /// - public class DisconnectedException : Exception - { - internal DisconnectedException(Exception innerException) : base(innerException.Message, innerException) - { } - } -} diff --git a/src/Linux/Tmds.DBus/Environment.cs b/src/Linux/Tmds.DBus/Environment.cs deleted file mode 100644 index a533411c62..0000000000 --- a/src/Linux/Tmds.DBus/Environment.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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; - } - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/IConnection.cs b/src/Linux/Tmds.DBus/IConnection.cs deleted file mode 100644 index 395710f32a..0000000000 --- a/src/Linux/Tmds.DBus/IConnection.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 -{ - /// - /// Interface of the Connection class. - /// - public interface IConnection : IDisposable - { - /// - Task ConnectAsync(); - - /// - T CreateProxy(string serviceName, ObjectPath2 path2); - - /// - event EventHandler StateChanged; - - /// - Task ListServicesAsync(); - - /// - Task ListActivatableServicesAsync(); - - /// - Task ResolveServiceOwnerAsync(string serviceName); - - /// - Task ResolveServiceOwnerAsync(string serviceName, Action handler, Action onError = null); - - /// - Task ActivateServiceAsync(string serviceName); - - /// - Task IsServiceActiveAsync(string serviceName); - - /// - Task QueueServiceRegistrationAsync(string serviceName, Action onAquired = null, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); - - /// - Task QueueServiceRegistrationAsync(string serviceName, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); - - /// - Task RegisterServiceAsync(string serviceName, Action onLost = null, ServiceRegistrationOptions options = ServiceRegistrationOptions.Default); - - /// - Task RegisterServiceAsync(string serviceName, ServiceRegistrationOptions options); - - /// - Task UnregisterServiceAsync(string serviceName); - - /// - Task RegisterObjectAsync(IDBusObject o); - - /// - Task RegisterObjectsAsync(IEnumerable objects); - - /// - void UnregisterObject(ObjectPath2 path2); - - /// - void UnregisterObject(IDBusObject dbusObject); - - /// - void UnregisterObjects(IEnumerable paths); - - /// - void UnregisterObjects(IEnumerable objects); - } -} diff --git a/src/Linux/Tmds.DBus/IDBus.cs b/src/Linux/Tmds.DBus/IDBus.cs deleted file mode 100644 index b66ad6c2c7..0000000000 --- a/src/Linux/Tmds.DBus/IDBus.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 ListActivatableNamesAsync(); - Task NameHasOwnerAsync(string name); - Task StartServiceByNameAsync(string name, uint flags); - Task GetNameOwnerAsync(string name); - Task ListNamesAsync(); - } -} diff --git a/src/Linux/Tmds.DBus/IDBusObject.cs b/src/Linux/Tmds.DBus/IDBusObject.cs deleted file mode 100644 index b3958995bd..0000000000 --- a/src/Linux/Tmds.DBus/IDBusObject.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - /// - /// Base interface for D-Bus objects. - /// - public interface IDBusObject - { - /// - /// Path of the D-Bus object. - /// - ObjectPath2 ObjectPath2 { get; } - } -} diff --git a/src/Linux/Tmds.DBus/Interop.cs b/src/Linux/Tmds.DBus/Interop.cs deleted file mode 100644 index da00e6c687..0000000000 --- a/src/Linux/Tmds.DBus/Interop.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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); - } -} diff --git a/src/Linux/Tmds.DBus/MessageHandler.cs b/src/Linux/Tmds.DBus/MessageHandler.cs deleted file mode 100644 index 4bac93a416..0000000000 --- a/src/Linux/Tmds.DBus/MessageHandler.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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); -} diff --git a/src/Linux/Tmds.DBus/MessageHelper.cs b/src/Linux/Tmds.DBus/MessageHelper.cs deleted file mode 100644 index fd86fe9b54..0000000000 --- a/src/Linux/Tmds.DBus/MessageHelper.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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; - } - } -} diff --git a/src/Linux/Tmds.DBus/ObjectPath2.cs b/src/Linux/Tmds.DBus/ObjectPath2.cs deleted file mode 100644 index 2d93c3e20f..0000000000 --- a/src/Linux/Tmds.DBus/ObjectPath2.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Path to D-Bus object. - /// - public struct ObjectPath2 : IComparable, IComparable, IEquatable - { - /// - /// Root path ("/"). - /// - public static readonly ObjectPath2 Root = new ObjectPath2("/"); - - internal readonly string Value; - - /// - /// Creates a new ObjectPath. - /// - /// path. - 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 == '/'; - } - - } - - /// - /// 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. - /// - public int CompareTo(ObjectPath2 other) - { - return Value.CompareTo(other.Value); - } - - /// - /// 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. - /// - public int CompareTo(object otherObject) - { - var other = otherObject as ObjectPath2?; - - if (other == null) - return 1; - - return Value.CompareTo(other.Value.Value); - } - - /// - /// Determines whether the specified object is equal to the current object. - /// - public bool Equals(ObjectPath2 other) - { - return Value == other.Value; - } - - /// - /// Determines whether the specified object is equal to the current object. - /// - public override bool Equals(object o) - { - var b = o as ObjectPath2?; - - if (b == null) - return false; - - return Value.Equals(b.Value.Value, StringComparison.Ordinal); - } - - /// - /// Determines whether two specified ObjectPaths have the same value. - /// - public static bool operator==(ObjectPath2 a, ObjectPath2 b) - { - return a.Value == b.Value; - } - - /// - /// Determines whether two specified ObjectPaths have different values. - /// - public static bool operator!=(ObjectPath2 a, ObjectPath2 b) - { - return !(a == b); - } - - /// - /// Returns the hash code for this ObjectPath. - /// - public override int GetHashCode() - { - if (Value == null) - { - return 0; - } - return Value.GetHashCode(); - } - - /// - /// Returns a string that represents the current object. - /// - public override string ToString() - { - return Value; - } - - /// - /// Creates the ObjectPath that is represented by the string value. - /// - /// path. - 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); - } - } - } -} diff --git a/src/Linux/Tmds.DBus/OwnerChangedMatchRule.cs b/src/Linux/Tmds.DBus/OwnerChangedMatchRule.cs deleted file mode 100644 index 38e202b452..0000000000 --- a/src/Linux/Tmds.DBus/OwnerChangedMatchRule.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(); - } - } -} diff --git a/src/Linux/Tmds.DBus/PropertyAccess.cs b/src/Linux/Tmds.DBus/PropertyAccess.cs deleted file mode 100644 index cf41d861b4..0000000000 --- a/src/Linux/Tmds.DBus/PropertyAccess.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - /// - /// The mutability of a property - /// - public enum PropertyAccess - { - /// - /// Allows the property to be read and written - /// - ReadWrite, - /// - /// Allows the property to only be read - /// - Read, - /// - /// Allows the property to only be written to - /// - Write - } -} diff --git a/src/Linux/Tmds.DBus/PropertyAttribute.cs b/src/Linux/Tmds.DBus/PropertyAttribute.cs deleted file mode 100644 index 1b9e21be1c..0000000000 --- a/src/Linux/Tmds.DBus/PropertyAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Tmds.DBus.CodeGen; - -namespace Tmds.DBus -{ - /// - /// Overrides how the property is handled. - /// - [AttributeUsage(AttributeTargets.Field)] - public sealed class PropertyAttribute : Attribute - { - /// - /// If not null, used to override the autogenerated name. - /// - public string Name { get; set; } - - /// - /// Specifies the mutability of the property. - /// - public PropertyAccess Access { get; set; } = PropertyAccess.ReadWrite; - } -} diff --git a/src/Linux/Tmds.DBus/PropertyChanges.cs b/src/Linux/Tmds.DBus/PropertyChanges.cs deleted file mode 100644 index 6813684613..0000000000 --- a/src/Linux/Tmds.DBus/PropertyChanges.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; -using System.Collections.Generic; - -namespace Tmds.DBus -{ - /// - /// Event data for the properties changed event. - /// - public struct PropertyChanges - { - private KeyValuePair[] _changed; - private string[] _invalidated; - - /// - /// Properties that have changed with their new value. - /// - public KeyValuePair[] Changed => _changed; - - /// - /// Properties that have changed. - /// - public string[] Invalidated => _invalidated; - - /// - /// Creates a PropertyChanges event. - /// - /// Properties that changed with their new value. - /// Properties that changed without providing new value. - public PropertyChanges(KeyValuePair[] changed,string[] invalidated = null) - { - _changed = changed ?? Array.Empty>(); - _invalidated = invalidated ?? Array.Empty(); - } - - /// - /// Creates a PropertyChanges event for a single value change. - /// - public static PropertyChanges ForProperty(string prop, object val) - => new PropertyChanges(new [] { new KeyValuePair(prop, val) }); - - /// - /// Retrieves value for a specific property. default(T) is returned when the property is not present. - /// - /// Type of the property. - /// Property name. - /// - /// Value of the property. default(T) when property not present. - /// - public T Get(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); - } - - /// - /// Retrieves value for a specific property. default(T?) is returned when the property is not present. - /// - /// Type of the property. - /// Property name. - /// - /// Value of the property. default(T?) when property not present. - /// - public T? GetStruct(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?); - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Protocol/DType.cs b/src/Linux/Tmds.DBus/Protocol/DType.cs deleted file mode 100644 index c2e130e37a..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/DType.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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)'}', - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/EndianFlag.cs b/src/Linux/Tmds.DBus/Protocol/EndianFlag.cs deleted file mode 100644 index ea06e29d97..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/EndianFlag.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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', - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/FieldCode.cs b/src/Linux/Tmds.DBus/Protocol/FieldCode.cs deleted file mode 100644 index 9ab6f08392..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/FieldCode.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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 - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/Header.cs b/src/Linux/Tmds.DBus/Protocol/Header.cs deleted file mode 100644 index 78e407a69e..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/Header.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// Copyright 2016 Tom Deseyn -// 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 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(); - 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> GetFields() - { - if (Path != null) - { - yield return new KeyValuePair(FieldCode.Path, Path.Value); - } - if (Interface != null) - { - yield return new KeyValuePair(FieldCode.Interface, Interface); - } - if (Member != null) - { - yield return new KeyValuePair(FieldCode.Member, Member); - } - if (ErrorName != null) - { - yield return new KeyValuePair(FieldCode.ErrorName, ErrorName); - } - if (ReplySerial != null) - { - yield return new KeyValuePair(FieldCode.ReplySerial, ReplySerial); - } - if (Destination != null) - { - yield return new KeyValuePair(FieldCode.Destination, Destination); - } - if (Sender != null) - { - yield return new KeyValuePair(FieldCode.Sender, Sender); - } - if (Signature != null) - { - yield return new KeyValuePair(FieldCode.Signature, Signature.Value); - } - if (NumberOfFds != 0) - { - yield return new KeyValuePair(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 - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/HeaderFlag.cs b/src/Linux/Tmds.DBus/Protocol/HeaderFlag.cs deleted file mode 100644 index fd2a100d26..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/HeaderFlag.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// 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, - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/IMessageStream.cs b/src/Linux/Tmds.DBus/Protocol/IMessageStream.cs deleted file mode 100644 index a222c4d7fe..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/IMessageStream.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 ReceiveMessageAsync(); - Task SendMessageAsync(Message message); - void TrySendMessage(Message message); - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Protocol/IProxyFactory.cs b/src/Linux/Tmds.DBus/Protocol/IProxyFactory.cs deleted file mode 100644 index 1f72fe9438..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/IProxyFactory.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus.Protocol -{ - interface IProxyFactory - { - T CreateProxy(string serviceName, ObjectPath2 path2); - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Protocol/IntrospectionWriter.cs b/src/Linux/Tmds.DBus/Protocol/IntrospectionWriter.cs deleted file mode 100644 index d65c452d7c..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/IntrospectionWriter.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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("\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(" \n", name); - } - - public void WriteInterfaceEnd() - { - _sb.Append(" \n"); - } - - public void WriteMethodStart(string name) - { - _sb.AppendFormat(" \n", name); - } - - public void WriteMethodEnd() - { - _sb.Append(" \n"); - } - - public void WriteInArg(string name, Signature signature) - { - _sb.AppendFormat(" \n", name, signature); - } - - public void WriteOutArg(string name, Signature signature) - { - _sb.AppendFormat(" \n", name, signature); - } - - public void WriteSignalStart(string name) - { - _sb.AppendFormat(" \n", name); - } - - public void WriteSignalEnd() - { - _sb.Append(" \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(" \n", name, signature, propAccess); - } - - public void WriteArg(string name, Signature signature) - { - _sb.AppendFormat(" \n", name, signature); - } - - public void WriteNodeStart(string name) - { - _sb.AppendFormat("\n", name); - } - - public void WriteNodeEnd() - { - _sb.Append("\n"); - } - - public void WriteLiteral(string value) - { - _sb.Append(value); - } - - public void WriteChildNode(string name) - { - _sb.AppendFormat(" \n", name); - } - - public override string ToString() - { - return _sb.ToString(); - } - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/Message.cs b/src/Linux/Tmds.DBus/Protocol/Message.cs deleted file mode 100644 index d40c07280e..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/Message.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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; - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/MessageReader.cs b/src/Linux/Tmds.DBus/Protocol/MessageReader.cs deleted file mode 100644 index 6e84911569..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/MessageReader.cs +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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 _data; - private readonly Message _message; - private readonly IProxyFactory _proxyFactory; - - private int _pos = 0; - private bool _skipNextStructPadding = false; - - static Dictionary s_isPrimitiveStruct = new Dictionary (); - - public MessageReader(EndianFlag endianness, ArraySegment data) - { - _endianness = endianness; - _data = data; - } - - public MessageReader (Message message, IProxyFactory proxyFactory) : - this(message.Header.Endianness, new ArraySegment(message.Body ?? Array.Empty())) - { - _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() - { - ObjectPath2 path2 = ReadObjectPath(); - return _proxyFactory.CreateProxy(_message.Header.Sender, path2); - } - - public T ReadEnum() - { - 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() - { - 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() - { - 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() - { - 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 ReadDictionary () - { - uint ln = ReadUInt32 (); - - if (ln > ProtocolInformation.MaxArrayLength) - throw new ProtocolException("Dict length " + ln + " exceeds maximum allowed " + ProtocolInformation.MaxArrayLength + " bytes"); - - var val = new Dictionary ((int)(ln / 8)); - ReadPad (8); - - int endPos = _pos + (int)ln; - - var keyReader = ReadMethodFactory.CreateReadMethodDelegate(); - var valueReader = ReadMethodFactory.CreateReadMethodDelegate(); - - 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 () - { - 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 (ln); - else - return (T[])(Array)MarshalBoolArray (ln); - } - - var list = new List (); - int endPos = _pos + (int)ln; - - var elementReader = ReadMethodFactory.CreateReadMethodDelegate(); - - while (_pos < endPos) - list.Add (elementReader(this)); - - if (_pos != endPos) - throw new ProtocolException("Read pos " + _pos + " != ep " + endPos); - - return list.ToArray (); - } - - TArray[] MarshalArray (uint length) - { - int sof = Marshal.SizeOf(); - 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 () - { - 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 (); - - foreach (System.Reflection.FieldInfo fi in fis) - fi.SetValue (val, Read (fi.FieldType)); - - return (T)val; - } - - public T ReadValueTupleStruct () - { - 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 (); - - 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; } - } - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/MessageType.cs b/src/Linux/Tmds.DBus/Protocol/MessageType.cs deleted file mode 100644 index 8867162b63..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/MessageType.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2010 Alan McGovern -// 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 - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/MessageWriter.cs b/src/Linux/Tmds.DBus/Protocol/MessageWriter.cs deleted file mode 100644 index c1c2b4bd4b..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/MessageWriter.cs +++ /dev/null @@ -1,608 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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 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(); - - 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(); - } - 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 (IEnumerable 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(); - - 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 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 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 (IEnumerable> 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(); - var valueWriter = WriteMethodFactory.CreateWriteMethodDelegate(); - - foreach (KeyValuePair 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 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> val) - { - long origPos = stream.Position; - WriteUInt32 ((uint)0); - - WritePad (8); - - long startPos = stream.Position; - - foreach (KeyValuePair 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); - } - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/MethodHandler.cs b/src/Linux/Tmds.DBus/Protocol/MethodHandler.cs deleted file mode 100644 index 4eeff24318..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/MethodHandler.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 MethodHandler(Message message); -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Protocol/ProtocolInformation.cs b/src/Linux/Tmds.DBus/Protocol/ProtocolInformation.cs deleted file mode 100644 index 58fec82c16..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/ProtocolInformation.cs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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); - } - } - } -} diff --git a/src/Linux/Tmds.DBus/Protocol/SignalHandler.cs b/src/Linux/Tmds.DBus/Protocol/SignalHandler.cs deleted file mode 100644 index f6d8b71633..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/SignalHandler.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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); -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Protocol/UnixFd.cs b/src/Linux/Tmds.DBus/Protocol/UnixFd.cs deleted file mode 100644 index c0e80f83b2..0000000000 --- a/src/Linux/Tmds.DBus/Protocol/UnixFd.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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; - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/ProtocolException.cs b/src/Linux/Tmds.DBus/ProtocolException.cs deleted file mode 100644 index e16768300d..0000000000 --- a/src/Linux/Tmds.DBus/ProtocolException.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Exception thrown when there is an error in the D-Bus protocol. - /// - public class ProtocolException : Exception - { - /// - /// Creates an instance of the ProtocolException with the specified message. - /// - /// The error message that explains the reason for the exception. - public ProtocolException(string message) : base(message) - {} - } -} diff --git a/src/Linux/Tmds.DBus/ReleaseNameReply.cs b/src/Linux/Tmds.DBus/ReleaseNameReply.cs deleted file mode 100644 index 3b91b6e00b..0000000000 --- a/src/Linux/Tmds.DBus/ReleaseNameReply.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - internal enum ReleaseNameReply : uint - { - ReplyReleased = 1, - NonExistent, - NotOwner - } -} diff --git a/src/Linux/Tmds.DBus/RequestNameOptions.cs b/src/Linux/Tmds.DBus/RequestNameOptions.cs deleted file mode 100644 index 234ed0921c..0000000000 --- a/src/Linux/Tmds.DBus/RequestNameOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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, - } -} diff --git a/src/Linux/Tmds.DBus/RequestNameReply.cs b/src/Linux/Tmds.DBus/RequestNameReply.cs deleted file mode 100644 index be0d49b360..0000000000 --- a/src/Linux/Tmds.DBus/RequestNameReply.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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, - } -} diff --git a/src/Linux/Tmds.DBus/ServerConnectionOptions.cs b/src/Linux/Tmds.DBus/ServerConnectionOptions.cs deleted file mode 100644 index b67ad88e22..0000000000 --- a/src/Linux/Tmds.DBus/ServerConnectionOptions.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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 -{ - /// - /// Options that configure the behavior of a Connection for a D-Bus local server. - /// - public class ServerConnectionOptions : ConnectionOptions - { - private Connection2 _connection2; - - /// - /// Starts the server at the specified address. - /// - /// Address of the D-Bus peer. - /// - /// Bound address. - /// - public Task StartAsync(string address) - => StartAsync(new ServerStartOptions { Address = address }); - - /// - /// Starts the server with the specified options. - /// - /// - /// - /// Bound address. - /// - public Task 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; - } - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/ServerStartOptions.cs b/src/Linux/Tmds.DBus/ServerStartOptions.cs deleted file mode 100644 index 7f6cc2565e..0000000000 --- a/src/Linux/Tmds.DBus/ServerStartOptions.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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 -{ - /// - /// Options that configure the behavior of ServerConnectionOptions.Start. - /// - public class ServerStartOptions : ConnectionOptions - { - /// - /// Listen address (e.g. 'tcp:host=localhost'). - /// - public string Address { get; set; } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/ServiceOwnerChangedEventArgs.cs b/src/Linux/Tmds.DBus/ServiceOwnerChangedEventArgs.cs deleted file mode 100644 index 232be0d9cb..0000000000 --- a/src/Linux/Tmds.DBus/ServiceOwnerChangedEventArgs.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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 -{ - /// - /// Event data for the ServiceOwnerChanged event. - /// - public struct ServiceOwnerChangedEventArgs - { - /// - /// Creates an instance of ServiceOwnerChangedEventArgs. - /// - /// The name of the service. - /// The previous owner of the service. - /// The new owner of the service. - public ServiceOwnerChangedEventArgs(string serviceName, string oldOwner, string newOwner) - { - ServiceName = serviceName; - OldOwner = oldOwner; - NewOwner = newOwner; - } - - /// - /// Name of the service. - /// - public string ServiceName { get; } - - /// - /// Local name of the previous owner. null when there is no previous owner. - /// - public string OldOwner { get; internal set; } - - /// - /// Local name of the new owner. null when there is no new owner. - /// - public string NewOwner { get; } - } -} diff --git a/src/Linux/Tmds.DBus/ServiceRegistrationOptions.cs b/src/Linux/Tmds.DBus/ServiceRegistrationOptions.cs deleted file mode 100644 index b715320df4..0000000000 --- a/src/Linux/Tmds.DBus/ServiceRegistrationOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -using System; - -namespace Tmds.DBus -{ - /// - /// Options for service name registration. - /// - [Flags] - public enum ServiceRegistrationOptions - { - /// No options. - None = 0, - /// Replace the existing owner. - ReplaceExisting = 1, - /// Allow registration to be replaced. - AllowReplacement = 2, - /// Default (ReplaceExisting | AllowReplacement) - Default = ReplaceExisting | AllowReplacement - } -} diff --git a/src/Linux/Tmds.DBus/ServiceStartResult.cs b/src/Linux/Tmds.DBus/ServiceStartResult.cs deleted file mode 100644 index 93ca3c770c..0000000000 --- a/src/Linux/Tmds.DBus/ServiceStartResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 Tom Deseyn -// This software is made available under the MIT License -// See COPYING for details - -namespace Tmds.DBus -{ - /// - /// Result of the service activation request. - /// - public enum ServiceStartResult : uint - { - /// The service was started. - Started = 1, - /// The service was already running. - AlreadyRunning = 2 - } -} diff --git a/src/Linux/Tmds.DBus/SignalMatchRule.cs b/src/Linux/Tmds.DBus/SignalMatchRule.cs deleted file mode 100644 index b69177f05a..0000000000 --- a/src/Linux/Tmds.DBus/SignalMatchRule.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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('\''); - } - } -} diff --git a/src/Linux/Tmds.DBus/SignalWatcher.cs b/src/Linux/Tmds.DBus/SignalWatcher.cs deleted file mode 100644 index ce88d8dba6..0000000000 --- a/src/Linux/Tmds.DBus/SignalWatcher.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Reflection; -using System.Threading.Tasks; - -namespace Tmds.DBus -{ - /// - /// Helper class for implementing D-Bus signals. - /// - 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); - } - - /// - /// Emits on the handler when the event is raised and returns an IDisposable that removes the handler. - /// - /// Object that emits events. - /// Name of the event. - /// Action to be invoked when the event is raised. - /// - /// Disposable that removes the handler from the event. - /// - public static Task AddAsync(object o, string eventName, Action handler) - { - return Task.FromResult(Add(o, eventName, handler)); - } - - /// - /// Emits on the handler when the event is raised and returns an IDisposable that removes the handler. - /// - /// Object that emits events. - /// Name of the event. - /// Action to be invoked when the event is raised. - /// - /// Disposable that removes the handler from the event. - /// - public static Task AddAsync(object o, string eventName, Action handler) - { - return Task.FromResult(Add(o, eventName, handler)); - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Signature.cs b/src/Linux/Tmds.DBus/Signature.cs deleted file mode 100644 index b78855d15f..0000000000 --- a/src/Linux/Tmds.DBus/Signature.cs +++ /dev/null @@ -1,899 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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 -{ - /// - /// D-Bus type signature. - /// - 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; - - /// - /// Determines whether two specified Signatures have the same value. - /// - 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; - } - - /// - /// Determines whether two specified Signatures have different values. - /// - public static bool operator!=(Signature a, Signature b) - { - return !(a == b); - } - - /// - /// Determines whether the specified object is equal to the current object. - /// - public override bool Equals (object o) - { - if (o == null) - return false; - - if (!(o is Signature)) - return false; - - return this == (Signature)o; - } - - /// - /// Returns the hash code for this Signature. - /// - 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); - } - - /// - /// Creates a new Signature. - /// - /// signature. - 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(); - } else if (value.Length == 1) { - _data = DataForDType ((DType)value[0]); - } else { - _data = Encoding.ASCII.GetBytes (value); - } - } - - /// - /// Creates a new Signature. - /// - /// signature. - 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]; - } - } - - /// - /// Length of the Signature. - /// - 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); - } - } - - /// - /// Returns a string that represents the current object. - /// - 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 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 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 fieldTypes = new List (); - 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 innerTypes = new List (); - 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; - } - } - } -} diff --git a/src/Linux/Tmds.DBus/Tmds.DBus.csproj b/src/Linux/Tmds.DBus/Tmds.DBus.csproj deleted file mode 100644 index 27f01f729a..0000000000 --- a/src/Linux/Tmds.DBus/Tmds.DBus.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - $(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0 - enable - true - - diff --git a/src/Linux/Tmds.DBus/Transports/LocalServer.cs b/src/Linux/Tmds.DBus/Transports/LocalServer.cs deleted file mode 100644 index 8b467fbb1b..0000000000 --- a/src/Linux/Tmds.DBus/Transports/LocalServer.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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(); - } - - public async Task 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 ReceiveMessageAsync() - { - return Task.FromException(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; - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Transports/Transport.cs b/src/Linux/Tmds.DBus/Transports/Transport.cs deleted file mode 100644 index b89b40f4f9..0000000000 --- a/src/Linux/Tmds.DBus/Transports/Transport.cs +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2006 Alp Toker -// Copyright 2016 Tom Deseyn -// 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 CompletionSource; - } - - private static readonly byte[] _oneByteArray = new[] { (byte)0 }; - private readonly byte[] _headerReadBuffer = new byte[16]; - private readonly List _fileDescriptors = new List(); - private TransportSocket _socket; - private ConcurrentQueue _sendQueue; - private SemaphoreSlim _sendSemaphore; - - public static async Task 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 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(); - _sendSemaphore = new SemaphoreSlim(1); - } - - public async Task 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(_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(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 ReadCountAsync(byte[] buffer, int offset, int count, List 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 _args = new List(); - - 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 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 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 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 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(); - 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(); - } - } -} \ No newline at end of file diff --git a/src/Linux/Tmds.DBus/Transports/TransportSocket.cs b/src/Linux/Tmds.DBus/Transports/TransportSocket.cs deleted file mode 100644 index 2e2496368d..0000000000 --- a/src/Linux/Tmds.DBus/Transports/TransportSocket.cs +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2017 Tom Deseyn -// 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 Tcs; - public byte[] Buffer; - public int Offset; - public int Count; - public List FileDescriptors; - } - - private class SendContext - { - public TaskCompletionSource 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> _bufferList = new List>(); - 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(), 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>(); - _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 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 ReadAsync(byte[] buffer, int offset, int count, List fileDescriptors) - { - if (!_supportsFdPassing) - { - var readContext = _receiveData.UserToken as ReadContext; - TaskCompletionSource tcs = readContext.Tcs ?? new TaskCompletionSource(); - 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(new SocketException((int)_receiveData.SocketError)); - } - } - else - { - return tcs.Task; - } - } - else - { - var readContext = _waitForData.UserToken as ReadContext; - TaskCompletionSource tcs = readContext.Tcs ?? new TaskCompletionSource(); - 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(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(headerBytes, 0, headerBytes.Length)); - if (message.Body != null) - { - _bufferList.Add(new ArraySegment(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(buffer, offset, count)); - return SendBufferListAsync(_bufferList); - } - - private Task SendBufferListAsync(List> bufferList) - { - var sendContext = _sendArgs.UserToken as SendContext; - TaskCompletionSource tcs = sendContext.Tcs ?? new TaskCompletionSource(); - 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 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 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 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); - } -} diff --git a/src/Linux/Tmds.DBus/Transports/UnixDomainSocketEndPoint.cs b/src/Linux/Tmds.DBus/Transports/UnixDomainSocketEndPoint.cs deleted file mode 100644 index 3cb82690d3..0000000000 --- a/src/Linux/Tmds.DBus/Transports/UnixDomainSocketEndPoint.cs +++ /dev/null @@ -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 -{ - /// Represents a Unix Domain Socket endpoint as a path. - 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(); - _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; - } -} diff --git a/src/Linux/Tmds.DBus/WrappedDisposable.cs b/src/Linux/Tmds.DBus/WrappedDisposable.cs deleted file mode 100644 index b132590b2c..0000000000 --- a/src/Linux/Tmds.DBus/WrappedDisposable.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2016 Tom Deseyn -// 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(Action 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; } - } - } - } -}