From c7aec2ea4138b1ed5e17df381674f3c2f3bd606e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 May 2026 13:51:52 -0700 Subject: [PATCH 1/9] Remove cswinrt native project and refs Remove the cswinrt native project and its references across the repo. Deleted the src/cswinrt project files and sources (Directory.Build.props/targets, headers, sources, IDL, generators, etc.), removed ProjectReference entries to cswinrt.vcxproj from multiple Projections and Test csproj files, and updated src/cswinrt.slnx to drop the cswinrt project and related build dependencies. This cleans up the solution by removing the legacy/native cswinrt component and its build hooks. --- src/Projections/Benchmark/Benchmark.csproj | 1 - src/Projections/Test/Test.csproj | 1 - .../TestHost.ProbeByClass.csproj | 1 - .../TestPublicExclusiveTo.csproj | 1 - src/Projections/TestSubset/TestSubset.csproj | 1 - src/Projections/WinAppSDK/WinAppSDK.csproj | 1 - .../Windows.UI.Xaml/Windows.UI.Xaml.csproj | 1 - src/Projections/Windows/Windows.csproj | 1 - src/Tests/AuthoringTest/AuthoringTest.csproj | 1 - .../AuthoringWuxTest/AuthoringWuxTest.csproj | 1 - src/cswinrt.slnx | 8 - src/cswinrt/Directory.Build.props | 11 - src/cswinrt/Directory.Build.targets | 21 - src/cswinrt/PreviousPlatforms.linq | 38 - src/cswinrt/WindowsRuntime.Internal.idl | 173 - src/cswinrt/cmd_reader.h | 686 - src/cswinrt/code_writers.h | 11356 ---------------- src/cswinrt/cswinrt.natvis | 22 - src/cswinrt/cswinrt.vcxproj | 94 - src/cswinrt/cswinrt.vcxproj.filters | 30 - src/cswinrt/guid_generator.h | 326 - src/cswinrt/helpers.h | 1838 --- src/cswinrt/main.cpp | 527 - src/cswinrt/packages.config | 5 - src/cswinrt/pch.cpp | 1 - src/cswinrt/pch.h | 8 - src/cswinrt/settings.h | 26 - src/cswinrt/task_group.h | 49 - src/cswinrt/text_writer.h | 520 - src/cswinrt/type_writers.h | 324 - src/cswinrt/version.rc | Bin 1558 -> 0 bytes 31 files changed, 16073 deletions(-) delete mode 100644 src/cswinrt/Directory.Build.props delete mode 100644 src/cswinrt/Directory.Build.targets delete mode 100644 src/cswinrt/PreviousPlatforms.linq delete mode 100644 src/cswinrt/WindowsRuntime.Internal.idl delete mode 100644 src/cswinrt/cmd_reader.h delete mode 100644 src/cswinrt/code_writers.h delete mode 100644 src/cswinrt/cswinrt.natvis delete mode 100644 src/cswinrt/cswinrt.vcxproj delete mode 100644 src/cswinrt/cswinrt.vcxproj.filters delete mode 100644 src/cswinrt/guid_generator.h delete mode 100644 src/cswinrt/helpers.h delete mode 100644 src/cswinrt/main.cpp delete mode 100644 src/cswinrt/packages.config delete mode 100644 src/cswinrt/pch.cpp delete mode 100644 src/cswinrt/pch.h delete mode 100644 src/cswinrt/settings.h delete mode 100644 src/cswinrt/task_group.h delete mode 100644 src/cswinrt/text_writer.h delete mode 100644 src/cswinrt/type_writers.h delete mode 100644 src/cswinrt/version.rc diff --git a/src/Projections/Benchmark/Benchmark.csproj b/src/Projections/Benchmark/Benchmark.csproj index 86865d2968..52a8afbbd3 100644 --- a/src/Projections/Benchmark/Benchmark.csproj +++ b/src/Projections/Benchmark/Benchmark.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj index 4668d8796a..aeb4a6c1e3 100644 --- a/src/Projections/Test/Test.csproj +++ b/src/Projections/Test/Test.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj index 3a6558e609..fd50f12287 100644 --- a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj +++ b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj @@ -9,7 +9,6 @@ - diff --git a/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj index 5367c787b8..e930fe5eba 100644 --- a/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj +++ b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj @@ -11,7 +11,6 @@ - diff --git a/src/Projections/TestSubset/TestSubset.csproj b/src/Projections/TestSubset/TestSubset.csproj index 23371f7bf1..cc1814ec6b 100644 --- a/src/Projections/TestSubset/TestSubset.csproj +++ b/src/Projections/TestSubset/TestSubset.csproj @@ -11,7 +11,6 @@ - diff --git a/src/Projections/WinAppSDK/WinAppSDK.csproj b/src/Projections/WinAppSDK/WinAppSDK.csproj index 188de263f2..cd3a80b7b8 100644 --- a/src/Projections/WinAppSDK/WinAppSDK.csproj +++ b/src/Projections/WinAppSDK/WinAppSDK.csproj @@ -8,7 +8,6 @@ - build; buildtransitive; compile; runtime diff --git a/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj index 1ade586088..a48dfeec94 100644 --- a/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj +++ b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj @@ -9,7 +9,6 @@ - diff --git a/src/Projections/Windows/Windows.csproj b/src/Projections/Windows/Windows.csproj index ee46556aa7..3e53c8922b 100644 --- a/src/Projections/Windows/Windows.csproj +++ b/src/Projections/Windows/Windows.csproj @@ -10,7 +10,6 @@ - diff --git a/src/Tests/AuthoringTest/AuthoringTest.csproj b/src/Tests/AuthoringTest/AuthoringTest.csproj index 72ed2eacc1..548250e59e 100644 --- a/src/Tests/AuthoringTest/AuthoringTest.csproj +++ b/src/Tests/AuthoringTest/AuthoringTest.csproj @@ -43,7 +43,6 @@ - \ No newline at end of file diff --git a/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj b/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj index 24e964724b..3cb0542da4 100644 --- a/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj +++ b/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj @@ -23,7 +23,6 @@ - \ No newline at end of file diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index 721a971c15..16594b48c8 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -19,7 +19,6 @@ - @@ -260,7 +259,6 @@ - @@ -274,7 +272,6 @@ - @@ -418,11 +415,6 @@ - - - - - diff --git a/src/cswinrt/Directory.Build.props b/src/cswinrt/Directory.Build.props deleted file mode 100644 index a073339701..0000000000 --- a/src/cswinrt/Directory.Build.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - %(AdditionalOptions) /D UAC_VERSION=99 - - - - diff --git a/src/cswinrt/Directory.Build.targets b/src/cswinrt/Directory.Build.targets deleted file mode 100644 index 5a487cd3f9..0000000000 --- a/src/cswinrt/Directory.Build.targets +++ /dev/null @@ -1,21 +0,0 @@ - - - - $(MidlRTCopyWinMDToOutputDirectoryDependsOn);CopyWinRTInteropWinMD - - - - - - - - - - - diff --git a/src/cswinrt/PreviousPlatforms.linq b/src/cswinrt/PreviousPlatforms.linq deleted file mode 100644 index 5b00c8aed3..0000000000 --- a/src/cswinrt/PreviousPlatforms.linq +++ /dev/null @@ -1,38 +0,0 @@ - - -// This script can be run periodically from within LINQPad (https://www.linqpad.net) -// to generate aggregate data for get_contract_platform in helpers.h - -var doc = XDocument.Load(@"C:\Program Files (x86)\Windows Kits\10\Platforms\UAP\10.0.26100.0\PreviousPlatforms.xml"); - -XNamespace pp = "http://microsoft.com/schemas/Windows/SDK/PreviousPlatforms"; - -var contracts = - from platform in doc.Elements().Elements() - let PlatformName = platform.Attribute("friendlyName").Value - let PlatformVersion = platform.Attribute("version").Value - from contract in platform.Elements(pp + "ContainedApiContracts").Elements() - let ContractName = contract.Attribute("name").Value - let ContractVersion = contract.Attribute("version").Value - let ContractNumber = int.Parse(ContractVersion.Replace(".0.0.0","")) - orderby ContractName, ContractNumber, PlatformVersion - group new{ContractNumber, PlatformVersion} by ContractName; - -foreach(var contract in contracts) -{ - String.Format( -@" {{ ""{0}"", - {{", contract.Key).Dump(); - int ContractNumber = 0; - foreach(var mapping in contract) - { - if(mapping.ContractNumber == ContractNumber) continue; - ContractNumber= mapping.ContractNumber; - String.Format( -@" {{ {0}, ""{1}"" }},", mapping.ContractNumber, mapping.PlatformVersion).Dump(); - } - -@" } - },".Dump(); -} - diff --git a/src/cswinrt/WindowsRuntime.Internal.idl b/src/cswinrt/WindowsRuntime.Internal.idl deleted file mode 100644 index 7c134f3316..0000000000 --- a/src/cswinrt/WindowsRuntime.Internal.idl +++ /dev/null @@ -1,173 +0,0 @@ -// Modern IDL 3.0: https://docs.microsoft.com/en-us/uwp/midl-3/intro - -// Note: the projection generated for this metadata should be internal, and only -// accessed indirectly with the user-friendly wrappers in ComInteropExtensions.cs. -namespace WindowsRuntime.Internal -{ - // C#/WinRT provides support for generating internal interface projections - [attributeusage(target_interface)] - [attributename("ProjectionInternal")] - attribute ProjectionInternalAttribute - { - } - - // C#/WinRT provides a custom mapping of WindowsRuntime.Internal.HWND to System.IntPtr - struct HWND - { - Int32 unused; - }; - - // accountssettingspaneinterop.idl (see: https://learn.microsoft.com/windows/win32/api/accountssettingspaneinterop/) - [uuid(D3EE12AD-3865-4362-9746-B75A682DF0E6), ProjectionInternal] - interface IAccountsSettingsPaneInterop - { - Windows.UI.ApplicationSettings.AccountsSettingsPane GetForWindow( - HWND appWindow, - ref const GUID riid); // __uuidof(AccountSettingsPanel) - - Windows.Foundation.IAsyncAction ShowManageAccountsForWindowAsync( - HWND appWindow, - ref const GUID riid); // __uuidof(IAsyncAction) - - Windows.Foundation.IAsyncAction ShowAddAccountForWindowAsync( - HWND appWindow, - ref const GUID riid); // __uuidof(IAsyncAction) - } - - // dragdropinterop.idl (see: https://learn.microsoft.com/windows/win32/api/dragdropinterop/) - [uuid(5AD8CBA7-4C01-4DAC-9074-827894292D63), ProjectionInternal] - interface IDragDropManagerInterop - { - Windows.ApplicationModel.DataTransfer.DragDrop.Core.CoreDragDropManager GetForWindow( - HWND hwnd, - ref const GUID riid); // __uuidof(CoreDragDropManager) - } - - // inputpaneinterop.idl (see: https://learn.microsoft.com/windows/win32/api/inputpaneinterop/) - [uuid(75CF2C57-9195-4931-8332-F0B409E916AF), ProjectionInternal] - interface IInputPaneInterop - { - Windows.UI.ViewManagement.InputPane GetForWindow( - HWND appWindow, - ref const GUID riid); // __uuidof(InputPane) - } - - // PlayToManagerInterop.idl (see: https://learn.microsoft.com/windows/win32/api/playtomanagerinterop/) - [uuid(24394699-1F2C-4EB3-8CD7-0EC1DA42A540), ProjectionInternal] - interface IPlayToManagerInterop - { - Windows.Media.PlayTo.PlayToManager GetForWindow( - HWND appWindow, - ref const GUID riid); // __uuidof(PlayToManager) - - void ShowPlayToUIForWindow( - HWND appWindow); - } - - // PrintManagerInterop.idl (see: https://learn.microsoft.com/windows/win32/api/printmanagerinterop/) - [uuid(c5435a42-8d43-4e7b-a68a-ef311e392087), ProjectionInternal] - interface IPrintManagerInterop - { - Windows.Graphics.Printing.PrintManager GetForWindow( - HWND appWindow, - ref const GUID riid); // __uuidof(PrintManager) - - Windows.Foundation.IAsyncOperation ShowPrintUIForWindowAsync( - HWND appWindow, - ref const GUID riid); // __uuidof(IAsyncOperation) - } - - // RadialControllerInterop.idl (see: https://learn.microsoft.com/windows/win32/api/radialcontrollerinterop/) - [uuid(1B0535C9-57AD-45C1-9D79-AD5C34360513), ProjectionInternal] - interface IRadialControllerInterop - { - Windows.UI.Input.RadialController CreateForWindow( - HWND hwnd, - ref const GUID riid); // __uuidof(RadialController) - } - - [uuid(787cdaac-3186-476d-87e4-b9374a7b9970), ProjectionInternal] - interface IRadialControllerConfigurationInterop - { - Windows.UI.Input.RadialControllerConfiguration GetForWindow( - HWND hwnd, - ref const GUID riid); // __uuidof(RadialControllerConfiguration) - } - - [uuid(3D577EFF-4CEE-11E6-B535-001BDC06AB3B), ProjectionInternal] - interface IRadialControllerIndependentInputSourceInterop - { - Windows.UI.Input.Core.RadialControllerIndependentInputSource CreateForWindow( - HWND hwnd, - ref const GUID riid); // __uuidof(RadialControllerIndependentInputSource) - } - - // SpatialInteractionManagerInterop.idl (see: https://learn.microsoft.com/windows/win32/api/spatialinteractionmanagerinterop/) - // This interop interface is duplicated by IHolographicSpaceInterop, which has the same IID - [uuid(5C4EE536-6A98-4B86-A170-587013D6FD4B), ProjectionInternal] - interface ISpatialInteractionManagerInterop - { - Windows.UI.Input.Spatial.SpatialInteractionManager GetForWindow( - HWND window, - ref const GUID riid); // __uuidof(SpatialInteractionManager) - } - - // SystemMediaTransportControlsInterop.idl (see: https://learn.microsoft.com/windows/win32/api/systemmediatransportcontrolsinterop/) - [uuid(ddb0472d-c911-4a1f-86d9-dc3d71a95f5a), ProjectionInternal] - interface ISystemMediaTransportControlsInterop - { - Windows.Media.SystemMediaTransportControls GetForWindow( - HWND appWindow, - ref const GUID riid); // __uuidof(SystemMediaTransportControls) - } - - // UIViewSettingsInterop.idl (see: https://learn.microsoft.com/windows/win32/api/uiviewsettingsinterop/) - [uuid(3694dbf9-8f68-44be-8ff5-195c98ede8a6), ProjectionInternal] - interface IUIViewSettingsInterop - { - Windows.UI.ViewManagement.UIViewSettings GetForWindow( - HWND hwnd, - ref const GUID riid); // __uuidof(UIViewSettings) - } - - // UserConsentVerifierInterop.idl (see: https://learn.microsoft.com/windows/win32/api/userconsentverifierinterop/) - [uuid(39E050C3-4E74-441A-8DC0-B81104DF949C), ProjectionInternal] - interface IUserConsentVerifierInterop - { - Windows.Foundation.IAsyncOperation RequestVerificationForWindowAsync( - HWND appWindow, - String message, - ref const GUID riid); // __uuidof(IAsyncOperation) - } - - // WebAuthenticationCoreManagerInterop.idl (see: https://learn.microsoft.com/windows/win32/api/webauthenticationcoremanagerinterop/) - [uuid(F4B8E804-811E-4436-B69C-44CB67B72084), ProjectionInternal] - interface IWebAuthenticationCoreManagerInterop - { - Windows.Foundation.IAsyncOperation RequestTokenForWindowAsync( - HWND appWindow, - Windows.Security.Authentication.Web.Core.WebTokenRequest request, - ref const GUID riid); // __uuidof(IAsyncOperation) - - Windows.Foundation.IAsyncOperation RequestTokenWithWebAccountForWindowAsync( - HWND appWindow, - Windows.Security.Authentication.Web.Core.WebTokenRequest request, - Windows.Security.Credentials.WebAccount webAccount, - ref const GUID riid); // __uuidof(IAsyncOperation) - } - - // WindowsGraphicsDisplayInterop.idl (see: https://learn.microsoft.com/windows/win32/api/windows.graphics.display.interop/) -#if UAC_VERSION > 14 - [uuid(7449121C-382B-4705-8DA7-A795BA482013), ProjectionInternal] - interface IDisplayInformationStaticsInterop - { - Windows.Graphics.Display.DisplayInformation GetForWindow( - HWND window, - ref const GUID riid); // __uuidof(DisplayInformation) - - Windows.Graphics.Display.DisplayInformation GetForMonitor( - HWND monitor, - ref const GUID riid); // __uuidof(DisplayInformation) - } -#endif -} \ No newline at end of file diff --git a/src/cswinrt/cmd_reader.h b/src/cswinrt/cmd_reader.h deleted file mode 100644 index edc406c9a5..0000000000 --- a/src/cswinrt/cmd_reader.h +++ /dev/null @@ -1,686 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cswinrt -{ - struct registry_key - { - HKEY handle{}; - - registry_key(registry_key const&) = delete; - registry_key& operator=(registry_key const&) = delete; - - ~registry_key() noexcept - { - if (handle) - { - RegCloseKey(handle); - } - } - }; - - template - struct com_ptr - { - T* ptr{}; - - com_ptr(com_ptr const&) = delete; - com_ptr& operator=(com_ptr const&) = delete; - - com_ptr() noexcept = default; - - ~com_ptr() noexcept - { - if (ptr) - { - ptr->Release(); - } - } - - auto operator->() const noexcept - { - return ptr; - } - }; - - static void check_xml(HRESULT result, const std::filesystem::path& platformXmlPath) - { - if (result < 0) - { - throw std::invalid_argument("Could not read the Windows SDK's Platform.xml at " + platformXmlPath.u8string()); - } - } - - inline void add_files_from_xml( - std::set& files, - std::string const& sdk_version, - std::filesystem::path const& xml_path, - std::filesystem::path const& sdk_path) - { - com_ptr stream; - - check_xml(SHCreateStreamOnFileW( - xml_path.c_str(), - STGM_READ, &stream.ptr), xml_path); - - com_ptr reader; - - check_xml(CreateXmlReader( - __uuidof(IXmlReader), - reinterpret_cast(&reader.ptr), - nullptr), xml_path); - - check_xml(reader->SetInput(stream.ptr), xml_path); - XmlNodeType node_type = XmlNodeType_None; - - while (S_OK == reader->Read(&node_type)) - { - if (node_type != XmlNodeType_Element) - { - continue; - } - - wchar_t const* value{ nullptr }; - check_xml(reader->GetLocalName(&value, nullptr), xml_path); - - if (0 != wcscmp(value, L"ApiContract")) - { - continue; - } - - auto path = sdk_path; - path /= L"References"; - path /= sdk_version; - - check_xml(reader->MoveToAttributeByName(L"name", nullptr), xml_path); - check_xml(reader->GetValue(&value, nullptr), xml_path); - path /= value; - - check_xml(reader->MoveToAttributeByName(L"version", nullptr), xml_path); - check_xml(reader->GetValue(&value, nullptr), xml_path); - path /= value; - - check_xml(reader->MoveToAttributeByName(L"name", nullptr), xml_path); - check_xml(reader->GetValue(&value, nullptr), xml_path); - path /= value; - - path += L".winmd"; - files.insert(path.string()); - } - } - - inline registry_key open_sdk() - { - HKEY key; - - if (0 != RegOpenKeyExW( - HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", - 0, - KEY_READ | KEY_WOW64_32KEY, - &key)) - { - if (0 != RegOpenKeyExW( - HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", - 0, - KEY_READ, - &key)) - { - throw std::invalid_argument("Could not find the Windows SDK in the registry"); - } - } - - return { key }; - } - - inline std::filesystem::path get_sdk_path() - { - auto key = open_sdk(); - - DWORD path_size = 0; - - if (0 != RegQueryValueExW( - key.handle, - L"KitsRoot10", - nullptr, - nullptr, - nullptr, - &path_size)) - { - throw std::invalid_argument("Could not find the Windows SDK path in the registry"); - } - - std::wstring root((path_size / sizeof(wchar_t)) - 1, L'?'); - - RegQueryValueExW( - key.handle, - L"KitsRoot10", - nullptr, - nullptr, - reinterpret_cast(root.data()), - &path_size); - - return root; - } - - inline std::string get_module_path() - { - std::string path(100, '?'); - DWORD actual_size{}; - - while (true) - { - actual_size = GetModuleFileNameA(nullptr, path.data(), 1 + static_cast(path.size())); - - if (actual_size < 1 + path.size()) - { - path.resize(actual_size); - break; - } - else - { - path.resize(path.size() * 2, '?'); - } - } - - return path; - } - - inline std::string get_sdk_version() - { - auto module_path = get_module_path(); - std::regex rx(R"(((\d+)\.(\d+)\.(\d+)\.(\d+)))"); - std::cmatch match; - auto sdk_path = get_sdk_path(); - - if (std::regex_search(module_path.c_str(), match, rx)) - { - auto path = sdk_path / "Platforms\\UAP" / match[1].str() / "Platform.xml"; - - if (std::filesystem::exists(path)) - { - return match[1].str(); - } - } - - auto key = open_sdk(); - uint32_t index{}; - std::array subkey; - std::array version_parts{}; - std::string result; - - while (0 == RegEnumKeyA(key.handle, index++, subkey.data(), static_cast(subkey.size()))) - { - if (!std::regex_match(subkey.data(), match, rx)) - { - continue; - } - - auto path = sdk_path / "Platforms\\UAP" / match[1].str() / "Platform.xml"; - if (!std::filesystem::exists(path)) - { - continue; - } - - char* next_part = subkey.data(); - bool force_newer = false; - - for (size_t i = 0; ; ++i) - { - auto version_part = strtoul(next_part, &next_part, 10); - - if ((version_part < version_parts[i]) && !force_newer) - { - break; - } - else if (version_part > version_parts[i]) - { - // E.g. ensure something like '2.1' is considered newer than '1.2' - force_newer = true; - } - - version_parts[i] = version_part; - - if (i == std::size(version_parts) - 1) - { - result = subkey.data(); - break; - } - - if (!next_part) - { - break; - } - - ++next_part; - } - } - - if (result.empty()) - { - throw std::invalid_argument("Could not find the Windows SDK"); - } - - return result; - } - - [[noreturn]] inline void throw_invalid(std::string const& message) - { - throw std::invalid_argument(message); - } - - template - [[noreturn]] inline void throw_invalid(std::string message, T const&... args) - { - (message.append(args), ...); - throw std::invalid_argument(message); - } - - struct option - { - static constexpr uint32_t no_min = 0; - static constexpr uint32_t no_max = std::numeric_limits::max(); - - std::string_view name; - uint32_t min{ no_min }; - uint32_t max{ no_max }; - std::string_view arg{}; - std::string_view desc{}; - }; - - struct reader - { - template - reader(C const argc, V const argv, const option(& options)[numOptions]) - { -#ifdef _DEBUG - { - std::set unique; - - for (auto&& option : options) - { - // If this assertion fails it means there are duplicate options. - assert(unique.insert(option.name).second); - } - } -#endif - - if (argc < 2) - { - return; - } - - auto last{ std::end(options) }; - - for (C i = 1; i < argc; ++i) - { - extract_option(argv[i], options, last); - } - - for (auto&& option : options) - { - auto args = m_options.find(option.name); - std::size_t const count = args == m_options.end() ? 0 : args->second.size(); - - if (option.min == 0 && option.max == 0 && count > 0) - { - throw_invalid("Option '", option.name, "' does not accept a value"); - } - else if (option.max == option.min && count != option.max) - { - throw_invalid("Option '", option.name, "' requires exactly ", std::to_string(option.max), " value(s)"); - } - else if (count < option.min) - { - throw_invalid("Option '", option.name, "' requires at least ", std::to_string(option.min), " value(s)"); - } - else if (count > option.max) - { - throw_invalid("Option '", option.name, "' accepts at most ", std::to_string(option.max), " value(s)"); - } - } - } - - explicit operator bool() const noexcept - { - return !m_options.empty(); - } - - bool exists(std::string_view const& name) const noexcept - { - return m_options.count(name); - } - - auto const& values(std::string_view const& name) const noexcept - { - auto result = m_options.find(name); - - if (result == m_options.end()) - { - static std::vector empty{}; - return empty; - } - - return result->second; - } - - auto value(std::string_view const& name, std::string_view const& default_value = {}) const - { - auto result = m_options.find(name); - - if (result == m_options.end() || result->second.empty()) - { - return std::string{ default_value }; - } - - return result->second.front(); - } - - template - auto files(std::string_view const& name, F directory_filter) const - { - std::set files; - - auto add_directory = [&](auto&& path) - { - for (auto&& file : std::filesystem::directory_iterator(path)) - { - if (std::filesystem::is_regular_file(file)) - { - auto filename = file.path().string(); - - if (directory_filter(filename)) - { - files.insert(filename); - } - } - } - }; - - for (auto&& path : values(name)) - { - if (std::filesystem::is_directory(path)) - { - try - { - add_directory(std::filesystem::canonical(path)); - } - catch (std::filesystem::filesystem_error const&) - { - // If canonical fails try using the provided path directly - add_directory(path); - } - continue; - } - - if (std::filesystem::is_regular_file(path)) - { - try - { - files.insert(std::filesystem::canonical(path).string()); - } - catch (std::filesystem::filesystem_error const&) - { - // If canonical fails try using the provided path directly - files.insert(path); - } - continue; - } - if (path == "local") - { - std::array local{}; -#ifdef _WIN64 - ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast(local.size())); -#else - ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast(local.size())); -#endif - add_directory(local.data()); - continue; - } - - std::string sdk_version; - - if (path == "sdk" || path == "sdk+") - { - sdk_version = get_sdk_version(); - } - else - { - std::regex rx(R"(((\d+)\.(\d+)\.(\d+)\.(\d+))\+?)"); - std::smatch match; - - if (std::regex_match(path, match, rx)) - { - sdk_version = match[1].str(); - } - } - - if (!sdk_version.empty()) - { - auto sdk_path = get_sdk_path(); - auto xml_path = sdk_path; - xml_path /= L"Platforms\\UAP"; - xml_path /= sdk_version; - xml_path /= L"Platform.xml"; - - add_files_from_xml(files, sdk_version, xml_path, sdk_path); - - if (path.back() != '+') - { - continue; - } - - for (auto&& item : std::filesystem::directory_iterator(sdk_path / L"Extension SDKs")) - { - xml_path = item.path() / sdk_version; - xml_path /= L"SDKManifest.xml"; - - add_files_from_xml(files, sdk_version, xml_path, sdk_path); - } - - continue; - } - - throw_invalid("Path '", path, "' is not a file or directory"); - } - - return files; - } - - auto files(std::string_view const& name) const - { - return files(name, [](auto&&) {return true; }); - } - - private: - - inline bool starts_with(std::string_view const& value, std::string_view const& match) noexcept - { - return 0 == value.compare(0, match.size(), match); - } - - template - auto find(O const& options, std::string_view const& arg) - { - for (auto current = std::begin(options); current != std::end(options); ++current) - { - if (starts_with(current->name, arg)) - { - return current; - } - } - - return std::end(options); - } - - std::map> m_options; - - template - void extract_option(std::string_view arg, O const& options, L& last) - { - if (arg[0] == '-' || arg[0] == '/') - { - arg.remove_prefix(1); - last = find(options, arg); - - if (last == std::end(options)) - { - throw_invalid("Option '-", arg, "' is not supported"); - } - - m_options.try_emplace(last->name); - } - else if (arg[0] == '@') - { - arg.remove_prefix(1); - extract_response_file(arg, options, last); - } - else if (last == std::end(options)) - { - throw_invalid("Value '", arg, "' is not supported"); - } - else - { - m_options[last->name].push_back(std::string{ arg }); - } - } - - template - void extract_response_file(std::string_view const& arg, O const& options, L& last) - { - std::filesystem::path response_path{ std::string{ arg } }; - std::string extension = response_path.extension().generic_string(); - std::transform(extension.begin(), extension.end(), extension.begin(), - [](auto c) { return static_cast(::tolower(c)); }); - - // Check if misuse of @ prefix, so if directory or metadata file instead of response file. - if (is_directory(response_path) || extension == ".winmd") - { - throw_invalid("'@' is reserved for response files"); - } - std::string line_buf; - std::ifstream response_file(absolute(response_path)); - while (getline(response_file, line_buf)) - { - size_t argc = 0; - std::vector argv; - - parse_command_line(line_buf.data(), argv, &argc); - - for (size_t i = 0; i < argc; i++) - { - extract_option(argv[i], options, last); - } - } - } - - static void parse_command_line(char* cmdstart, std::vector& argv, size_t* argument_count) - { - std::string arg; - bool copy_character; - unsigned backslash_count; - bool in_quotes; - bool first_arg; - - char* p = cmdstart; - if (*p == '#') - { - return; - } - - in_quotes = false; - first_arg = true; - *argument_count = 0; - - for (;;) - { - if (*p) - { - while (*p == ' ' || *p == '\t') - ++p; - } - - if (!first_arg) - { - argv.emplace_back(arg); - arg.clear(); - ++*argument_count; - } - - if (*p == '\0') - break; - - for (;;) - { - copy_character = true; - - // Rules: - // 2N backslashes + " ==> N backslashes and begin/end quote - // 2N + 1 backslashes + " ==> N backslashes + literal " - // N backslashes ==> N backslashes - backslash_count = 0; - - while (*p == '\\') - { - ++p; - ++backslash_count; - } - - if (*p == '"') - { - // if 2N backslashes before, start/end quote, otherwise - // copy literally: - if (backslash_count % 2 == 0) - { - if (in_quotes && p[1] == '"') - { - p++; // Double quote inside quoted string - } - else - { - // Skip first quote char and copy second: - copy_character = false; - in_quotes = !in_quotes; - } - } - - backslash_count /= 2; - } - - while (backslash_count--) - { - arg.push_back('\\'); - } - - if (*p == '\0' || (!in_quotes && (*p == ' ' || *p == '\t'))) - break; - - if (copy_character) - { - arg.push_back(*p); - } - - ++p; - } - - first_arg = false; - } - } - }; -} diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h deleted file mode 100644 index a780db203e..0000000000 --- a/src/cswinrt/code_writers.h +++ /dev/null @@ -1,11356 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "guid_generator.h" - -#define INSPECTABLE_METHOD_COUNT 6 - -namespace cswinrt -{ - using namespace winmd::reader; - - static const struct - { - char const* csharp; - char const* dotnet; - } - type_mappings[] = - { - {"bool", "Boolean"}, - {"char", "Char"}, - {"sbyte", "SByte"}, - {"byte", "Byte"}, - {"short", "Int16"}, - {"ushort", "UInt16"}, - {"int", "Int32"}, - {"uint", "UInt32"}, - {"long", "Int64"}, - {"ulong", "UInt64"}, - {"float", "Single"}, - {"double", "Double"}, - {"string", "String"}, - }; - - static concurrency::concurrent_unordered_set generic_type_instances; - generic_type_instance ConvertGenericTypeInstanceToConcreteType(writer& w, const generic_type_instance& generic_instance); - - auto to_csharp_type(fundamental_type type) - { - return type_mappings[(int)type].csharp; - } - - auto to_dotnet_type(fundamental_type type) - { - return type_mappings[(int)type].dotnet; - } - - auto get_delegate_type_suffix(fundamental_type type) - { - if (type == fundamental_type::String) - { - return "String"; - } - return type_mappings[(int)type].dotnet; - } - - static uint32_t get_vmethod_index(TypeDef const& type, MethodDef const& method) - { - uint32_t const vtable_base = type.MethodList().first.index(); - uint32_t const vtable_index = method.index() - vtable_base; - return vtable_index; - } - - static std::string get_vmethod_name(writer& w, TypeDef const& type, MethodDef const& method) - { - return w.write_temp("%_%", method.Name(), get_vmethod_index(type, method)); - } - - std::string internal_accessibility() - { - return (settings.internal || settings.embedded) ? "internal" : "public"; - } - - bool is_type_blittable(type_semantics const& semantics) - { - return call(semantics, - [&](object_type) - { - return false; - }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::enum_type: - return true; - case category::struct_type: - if (auto mapping = get_mapped_type(type.TypeNamespace(), type.TypeName())) - { - return !mapping->requires_marshaling; - } - - for (auto&& field : type.FieldList()) - { - if (!is_type_blittable(get_type_semantics(field.Signature().Type()))) - { - return false; - } - } - return true; - default: - return false; - } - }, - [&](generic_type_instance const& /*type*/) - { - return false; - }, - [&](fundamental_type const& type) - { - return (type != fundamental_type::String); - }, - [&](auto&&) - { - return true; - }); - } - - bool is_value_type(type_semantics const& semantics) - { - return call(semantics, - [&](object_type) - { - return false; - }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::enum_type: - return true; - case category::struct_type: - if (auto mapping = get_mapped_type(type.TypeNamespace(), type.TypeName())) - { - return true; - } - - for (auto&& field : type.FieldList()) - { - if (!is_value_type(get_type_semantics(field.Signature().Type()))) - { - return false; - } - } - return true; - default: - return false; - } - }, - [&](generic_type_instance const& /*type*/) - { - return false; - }, - [&](fundamental_type const& type) - { - return (type != fundamental_type::String); - }, - [&](auto&&) - { - return true; - }); - } - - bool use_tracker_object_support(type_semantics const& semantics) - { - return call(semantics, - [&](generic_type_instance const& type) - { - if (type.generic_type.TypeName() == "IReference`1") - { - return true; - } - return false; - }, - [&](type_definition const& type) - { - if (type.TypeName() == "HResult") - { - return true; - } - - switch (get_category(type)) - { - case category::struct_type: - for (auto&& field : type.FieldList()) - { - if (use_tracker_object_support(get_type_semantics(field.Signature().Type()))) - { - return true; - } - } - return false; - default: - return false; - } - }, - [&](auto&&) - { - return false; - }); - } - - // This checks for interfaces that have derived generic interfaces - // to handle the scenario where the class implementing that interface - // can be trimmed and we need to handle potential calls to the - // generic interface methods while not using IDIC. - bool has_derived_generic_interface(TypeDef const& type) - { - if (is_exclusive_to(type)) - { - return false; - } - - bool found = false; - - // Looking at derived interfaces and not the interface / type itself. - auto impls = type.InterfaceImpl(); - for (auto&& impl : impls) - { - call(get_type_semantics(impl.Interface()), - [&](type_definition const& type) - { - found |= has_derived_generic_interface(type); - }, - [&](generic_type_instance const&) - { - found = true; - }, - [](auto) - { - }); - - if (found) - { - return true; - } - } - - return false; - } - - void write_fundamental_type(writer& w, fundamental_type type) - { - w.write(to_csharp_type(type)); - } - - void write_fundamental_non_projected_type(writer& w, fundamental_type type) - { - w.write(to_dotnet_type(type)); - } - - void write_projection_type(writer& w, type_semantics const& semantics); - void write_projection_type_for_name_type(writer& w, type_semantics const& semantics, typedef_name_type const& nameType); - void write_guid(writer& w, TypeDef const& type, bool lowerCase); - - void write_generic_type_name_base(writer& w, uint32_t index) - { - write_projection_type(w, w.get_generic_arg_scope(index).first); - } - - void write_generic_type_name(writer& w, uint32_t index) - { - w.write_generic_type_name_custom ? - w.write_generic_type_name_custom(w, index) : - write_generic_type_name_base(w, index); - } - - template> - TResult for_typedef(writer& w, type_semantics const& semantics, TAction action) - { - return call(semantics, - [&](type_definition const& type) - { - return action(type); - }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - return action(type.generic_type); - }, - #pragma warning(disable:4702) - [](auto) - { - throw_invalid("type definition expected"); - return TResult(); - }); - } - - void write_typedef_name(writer& w, type_definition const& type, typedef_name_type const& nameType = typedef_name_type::Projected, bool forceWriteNamespace = false) - { - bool authoredType = settings.component && settings.filter.includes(type); - auto typeNamespace = type.TypeNamespace(); - auto typeName = type.TypeName(); - - if (nameType == typedef_name_type::NonProjected) - { - w.write("%.%", typeNamespace, typeName); - return; - } - - if (auto proj = get_mapped_type(typeNamespace, typeName)) - { - typeNamespace = proj->mapped_namespace; - typeName = proj->mapped_name; - } - - // Exclusive interfaces for authored types only exist in the CCW impl namepsace. - // For the default interface, if the projected name is requested, use the class type. - // For other exclusive interfaces, if the projected name is requested, use the CCW type. - bool use_exclusive_to_type = false; - TypeDef exclusive_to_type; - typedef_name_type name_type_to_write = nameType; - if (authoredType && is_exclusive_to(type) && name_type_to_write == typedef_name_type::Projected) - { - auto exclusive_to_attr = get_attribute(type, "Windows.Foundation.Metadata", "ExclusiveToAttribute"); - auto sig = exclusive_to_attr.Value(); - auto const& fixed_args = sig.FixedArgs(); - XLANG_ASSERT(fixed_args.size() == 1); - auto sys_type = std::get(std::get(fixed_args[0].value).value); - exclusive_to_type = type.get_cache().find_required(sys_type.name); - - try - { - if (auto default_interface = get_default_interface(exclusive_to_type)) - { - for_typedef(w, get_type_semantics(default_interface), [&](auto&& interface_type) - { - use_exclusive_to_type = (type == interface_type); - }); - } - } - catch (const std::invalid_argument&) - { - } - - if (!use_exclusive_to_type) - { - name_type_to_write = typedef_name_type::CCW; - } - } - - // Authored interfaces that aren't exclusive just use the same authored interface. - // It is only for exclusive ones where we generate our own. - if (authoredType && - name_type_to_write == typedef_name_type::CCW && - get_category(type) == category::interface_type && - !is_exclusive_to(type)) - { - name_type_to_write = typedef_name_type::Projected; - } - - if (name_type_to_write == typedef_name_type::EventSource && typeNamespace == "System") - { - w.write("global::WindowsRuntime.InteropServices."); - } - else if (forceWriteNamespace || - (typeNamespace != w._current_namespace) || - (name_type_to_write == typedef_name_type::Projected && (w._in_abi_namespace || w._in_abi_impl_namespace)) || - (name_type_to_write == typedef_name_type::ABI && !w._in_abi_namespace) || - (name_type_to_write == typedef_name_type::EventSource && !w._in_abi_namespace) || - (name_type_to_write == typedef_name_type::CCW && authoredType && !w._in_abi_impl_namespace) || - (name_type_to_write == typedef_name_type::CCW && !authoredType && (w._in_abi_namespace || w._in_abi_impl_namespace))) - { - w.write("global::"); - if (name_type_to_write == typedef_name_type::ABI || name_type_to_write == typedef_name_type::StaticAbiClass || name_type_to_write == typedef_name_type::EventSource) - { - w.write("ABI."); - } - else if (authoredType && name_type_to_write == typedef_name_type::CCW) - { - w.write("ABI.Impl."); - } - - w.write("%.", typeNamespace); - } - - if (use_exclusive_to_type) - { - w.write("@", exclusive_to_type.TypeName()); - } - else if (name_type_to_write == typedef_name_type::StaticAbiClass) - { - w.write("@%", typeName, "Methods"); - } - else if (name_type_to_write == typedef_name_type::EventSource) - { - w.write("@%", typeName, "EventSource"); - } - else - { - w.write("@", typeName); - } - } - - void write_type_params(writer& w, TypeDef const& type) - { - if (distance(type.GenericParam()) == 0) - { - return; - } - separator s{ w }; - uint32_t index = 0; - w.write("<%>", bind_each([&](writer& w, GenericParam const& /*gp*/) - { s(); write_generic_type_name(w, index++); }, type.GenericParam())); - } - - void write_type_name(writer& w, type_semantics const& semantics, typedef_name_type const& nameType = typedef_name_type::Projected, bool forceWriteNamespace = false) - { - // When forcing namespace qualification, temporarily clear the current namespace - // so that all type references (including generic type arguments) are fully qualified. - auto saved_namespace = forceWriteNamespace ? w._current_namespace : std::string_view{}; - if (forceWriteNamespace) w._current_namespace = {}; - - for_typedef(w, semantics, [&](auto type) - { - write_typedef_name(w, type, nameType, forceWriteNamespace); - write_type_params(w, type); - }); - - if (forceWriteNamespace) w._current_namespace = saved_namespace; - } - - auto write_type_name_temp(writer& w, type_semantics const& type, char const* format = "%", typedef_name_type const& nameType = typedef_name_type::Projected) - { - return w.write_temp(format, bind(type, nameType, false)); - } - - void write_interop_assembly_name(writer& w, TypeDef const& type) - { - auto typeNamespace = type.TypeNamespace(); - if (auto proj = get_mapped_type(typeNamespace, type.TypeName())) - { - typeNamespace = proj->mapped_namespace; - if (starts_with(typeNamespace, "System")) - { - if (is_mapped_type_in_system_numerics_vectors(typeNamespace)) - { - w.write(""); - } - else if (is_mapped_type_in_system_objectmodel(typeNamespace, proj->mapped_name)) - { - w.write(""); - } - else - { - w.write("<#corlib>"); - } - return; - } - else if (!proj->emit_abi) - { - w.write("<#CsWinRT>"); - return; - } - else if (starts_with(typeNamespace, "Windows")) - { - w.write("<#%Windows>"); - return; - } - } - - if (starts_with(typeNamespace, "Windows")) - { - w.write("<#Windows>"); - } - else - { - std::filesystem::path db_path(type.get_database().path()); - auto metadata = db_path.stem().string(); - - // Replace namespace seperator with _ within the generic due to that is what interop dll uses. - std::replace(metadata.begin(), metadata.end(), '.', '-'); - - w.write("<%>", metadata); - } - } - - void write_interop_dll_type_name(writer& w, type_semantics const& semantics, typedef_name_type const& nameType); - void write_interop_dll_type_name_for_typedef(writer& w, type_definition const& type, typedef_name_type const& nameType, std::vector generic_args = {}) - { - if (nameType != typedef_name_type::Projected && nameType != typedef_name_type::InteropIID) - { - w.write("ABI."); - } - - if (nameType == typedef_name_type::EventSource && type.TypeNamespace() == "Windows.Foundation") - { - w.write("WindowsRuntime.InteropServices.<#CsWinRT>EventHandlerEventSource'%", size(type.GenericParam())); - } - else - { - auto typeNamespace = std::string(type.TypeNamespace()); - auto typeName = std::string(type.TypeName()); - if (auto proj = get_mapped_type(typeNamespace, typeName)) - { - typeNamespace = std::string(proj->mapped_namespace); - typeName = std::string(proj->mapped_name); - } - - // Replace generic arity with ' due to that is what interop dll uses. - std::replace(typeName.begin(), typeName.end(), '`', '\''); - - // Arrays types are enclosed in <> - if (nameType == typedef_name_type::ArrayMarshaller) - { - w.write("%.<%%", typeNamespace, bind(type), typeName); - } - else if (nameType == typedef_name_type::InteropIID) - { - w.write("%%", bind(type), typeName); - } - else if (nameType == typedef_name_type::Projected) - { - // Replace namespace seperator with _ within the generic due to that is what interop dll uses. - std::replace(typeNamespace.begin(), typeNamespace.end(), '.', '-'); - w.write("%%-%", bind(type), typeNamespace, typeName); - } - else - { - w.write("%.%%", typeNamespace, bind(type), typeName); - } - } - - if (size(type.GenericParam()) != 0) - { - if (generic_args.size() != 0) - { - w.write("<%>", - bind_list("|", generic_args, typedef_name_type::Projected)); - } - else - { - separator s{ w, "|" }; - int index = 0; - w.write("<%>", - bind_each([&](writer& w, GenericParam const& /*gp*/) - { - s(); - write_interop_dll_type_name(w, w.get_generic_arg_scope(index++).first, typedef_name_type::Projected); - }, type.GenericParam())); - } - } - - // Arrays types are enclosed in <> - if (nameType == typedef_name_type::ArrayMarshaller) - { - w.write(">"); - } - } - - void write_interop_dll_type_name(writer& w, type_semantics const& semantics, typedef_name_type const& nameType) - { - call(semantics, - [&](object_type) - { - if (nameType == typedef_name_type::Projected) - { - w.write("object"); - } - else - { - w.write("ABI.System."); - } - }, - [&](guid_type) - { - if (nameType == typedef_name_type::Projected) - { - w.write("System-Guid"); - } - else - { - w.write("ABI.System.<<#corlib>Guid>"); - } - }, - [&](type_type) - { - if (nameType == typedef_name_type::Projected) - { - w.write("Type"); - } - else - { - w.write("ABI.System.<<#corlib>Type>"); - } - }, - [&](type_definition const& type) { write_interop_dll_type_name_for_typedef(w, type, nameType); }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - write_interop_dll_type_name_for_typedef(w, type.generic_type, nameType, type.generic_args); - }, - [&](fundamental_type const& type) - { - if (nameType == typedef_name_type::Projected) - { - write_fundamental_type(w, type); - } - else - { - w.write("ABI.System.<%>", bind(type)); - } - }, - [&](auto const&) {}); - - if (nameType == typedef_name_type::StaticAbiClass) - { - w.write("Methods"); - } - else if (nameType == typedef_name_type::Marshaller) - { - w.write("Marshaller"); - } - else if (nameType == typedef_name_type::ArrayMarshaller) - { - w.write("ArrayMarshaller"); - } - } - - void write_static_abi_class_generic_instantiation_type(writer& w, type_semantics const& semantics) - { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); - w.write(", "); - write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); - w.write("Abi"); - }); - - w.write("%", bind(semantics, typedef_name_type::StaticAbiClass, false)); - } - - void write_projection_type_for_name_type(writer& w, type_semantics const& semantics, typedef_name_type const& nameType) - { - call(semantics, - [&](object_type) { w.write("object"); }, - [&](guid_type) { w.write("Guid"); }, - [&](type_type) { w.write("Type"); }, - [&](type_definition const& type) { write_typedef_name(w, type, nameType); }, - [&](generic_type_index const& var) { write_generic_type_name(w, var.index); }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - w.write("%<%>", - bind(type.generic_type, nameType), - bind_list(", ", type.generic_args, nameType)); - }, - [&](generic_type_param const& param) { w.write(param.Name()); }, - [&](fundamental_type const& type) - { - if (nameType == typedef_name_type::NonProjected) - { - write_fundamental_non_projected_type(w, type); - } - else - { - write_fundamental_type(w, type); - } - }); - } - - void write_projection_type(writer& w, type_semantics const& semantics) - { - write_projection_type_for_name_type(w, semantics, typedef_name_type::Projected); - } - - void write_projection_ccw_type(writer& w, type_semantics const& semantics) - { - write_projection_type_for_name_type(w, semantics, typedef_name_type::CCW); - } - - std::string get_generic_instantiation_class_type_name(writer& w, type_definition const& type) - { - separator s{ w }; - uint32_t index = 0; - auto generic_instantiation_class_name = escape_type_name_for_identifier(w.write_temp( - "%_%", - bind(type, typedef_name_type::NonProjected, true), - bind_each([&](writer& w, GenericParam const& /*gp*/) - { - s(); - write_projection_type_for_name_type(w, w.get_generic_arg(index++), typedef_name_type::NonProjected); - }, type.GenericParam()))); - return generic_instantiation_class_name; - } - - bool is_keyword(std::string_view str) - { - // C# reserved keywords (sorted). Contextual keywords (e.g. 'value', 'var', - // 'dynamic') are intentionally excluded: they are valid identifiers outside - // their specific syntax contexts and do not need '@' escaping. - // See: https://learn.microsoft.com/dotnet/csharp/language-reference/keywords - static constexpr std::string_view keywords[] = - { - "abstract", "as", "base", "bool", "break", "byte", - "case", "catch", "char", "checked", "class", "const", - "continue", "decimal", "default", "delegate", "do", "double", - "else", "enum", "event", "explicit", "extern", "false", - "finally", "fixed", "float", "for", "foreach", "goto", - "if", "implicit", "in", "int", "interface", "internal", - "is", "lock", "long", "namespace", "new", "null", - "object", "operator", "out", "override", "params", "private", - "protected", "public", "readonly", "ref", "return", "sbyte", - "sealed", "short", "sizeof", "stackalloc", "static", "string", - "struct", "switch", "this", "throw", "true", "try", - "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", - "using", "virtual", "void", "volatile", "while" - }; -#if 0 - assert(std::is_sorted(std::begin(keywords), std::end(keywords))); -#endif - return std::binary_search(std::begin(keywords), std::end(keywords), str); - } - - void write_escaped_identifier(writer& w, std::string_view identifier) - { - if (is_keyword(identifier)) - { - w.write("@"); - } - w.write(identifier); - } - - void write_parameter_name(writer& w, method_signature::param_t const& param) - { - write_escaped_identifier(w, param.first.Name()); - } - - void write_parameter_name_with_modifier(writer& w, method_signature::param_t const& param) - { - switch (get_param_category(param)) - { - case param_category::ref: - w.write("in "); - break; - case param_category::out: - case param_category::receive_array: - w.write("out "); - break; - default: - break; - } - write_parameter_name(w, param); - } - - // This is used to handle scenarios where we do boxing with parameters passed to the constructor. - // This means for pass_array which we project as a ReadOnlySpan, we need to box the address of it. - void write_constructor_parameter_name_with_modifier(writer& w, method_signature::param_t const& param) - { - if (get_param_category(param) == param_category::pass_array) - { - w.write("(nint)(&%)", bind(param)); - } - else - { - write_parameter_name_with_modifier(w, param); - } - } - - void write_projection_parameter_type(writer& w, method_signature::param_t const& param) - { - auto semantics = get_type_semantics(param.second->Type()); - - switch (get_param_category(param)) - { - case param_category::in: - w.write("%", bind(semantics)); - break; - case param_category::ref: - w.write("in %", bind(semantics)); - break; - case param_category::out: - w.write("out %", bind(semantics)); - break; - case param_category::pass_array: - w.write("ReadOnlySpan<%>", bind(semantics)); - break; - case param_category::fill_array: - w.write("Span<%>", bind(semantics)); - break; - case param_category::receive_array: - w.write("out %[]", bind(semantics)); - break; - } - } - - // This is similar to write_constructor_parameter_name_with_modifier but used in the callback. - void write_constructor_parameter_cast(writer& w, method_signature::param_t const& param) - { - if (get_param_category(param) == param_category::pass_array) - { - auto semantics = get_type_semantics(param.second->Type()); - w.write("*(ReadOnlySpan<%>*)(nint)", bind(semantics)); - } - else - { - w.write("(%)", bind(param)); - } - } - - void write_constructor_args_field_type(writer& w, method_signature::param_t const& param) - { - auto semantics = get_type_semantics(param.second->Type()); - if (get_param_category(param) == param_category::pass_array) - { - w.write("ReadOnlySpan<%>", bind(semantics)); - } - else - { - write_projection_type(w, semantics); - } - } - - // Used as part of return type and property signatures - void write_projected_signature(writer& w, TypeSig const& type_sig, bool is_parameter) - { - // For parameters, we are only called in property scenarios where arrays are pass_array. - if (is_parameter && type_sig.is_szarray()) - { - w.write("ReadOnlySpan<%>", bind(get_type_semantics(type_sig))); - } - else - { - write_projection_type(w, get_type_semantics(type_sig)); - if (type_sig.is_szarray()) w.write("[]"); - } - }; - - void write_projection_return_type(writer& w, method_signature const& signature) - { - if (auto return_sig = signature.return_signature()) - { - write_projected_signature(w, return_sig.Type(), false); - } - else - { - w.write("void"); - } - } - - void write_projection_parameter(writer& w, method_signature::param_t const& param) - { - w.write("% %", - bind(param), - bind(param)); - } - - void write_parmaeters(writer& w, method_signature::param_t const& param) - { - switch (get_param_category(param)) - { - case param_category::in: - w.write("%", bind(param)); - break; - case param_category::ref: - w.write("in %", bind(param)); - break; - case param_category::out: - w.write("out %", bind(param)); - break; - case param_category::pass_array: - case param_category::fill_array: - w.write("%", bind(param)); - break; - case param_category::receive_array: - w.write("out %", bind(param)); - break; - } - } - - void write_guid_signature(writer& w, type_semantics const& semantics) - { - call(semantics, - [&](guid_type) - { - w.write("g16"); - }, - [&](object_type) - { - w.write("cinterface(IInspectable)"); - }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::enum_type: - { - w.write("enum(%;%)", - bind(type, typedef_name_type::NonProjected, true), - is_flags_enum(type) ? "u4" : "i4"); - break; - } - case category::struct_type: - { - w.write("struct(%;%)", - bind(type, typedef_name_type::NonProjected, true), - bind_list([](writer& w, Field const& field) - { - write_guid_signature(w, get_type_semantics(field.Signature().Type())); - }, ";", type.FieldList()) - ); - break; - } - case category::delegate_type: - { - w.write("delegate({%})", bind(type, true)); - break; - } - case category::interface_type: - { - w.write("{%}", bind(type, true)); - break; - } - case category::class_type: - { - if (auto default_interface = get_default_interface(type)) - { - w.write("rc(%;%)", - bind(type, typedef_name_type::NonProjected, true), - bind(get_type_semantics(default_interface))); - } - else - { - w.write("{%}", bind(type, true)); - } - break; - } - } - }, - [&](generic_type_instance const& type) - { - w.write("pinterface({%};%)", - bind(type.generic_type, true), - bind_list([](writer& w, type_semantics const& genericType) - { - write_guid_signature(w, genericType); - }, ";", type.generic_args) - ); - }, - [&](fundamental_type const& type) - { - w.write("%", get_fundamental_type_guid_signature(type)); - }, - [&](auto const&) {}); - } - - void write_guid(writer& w, TypeDef const& type, bool lowerCase) - { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); - } - - auto args = attribute.Value().FixedArgs(); - - using std::get; - - auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - - w.write_printf( - lowerCase ? - R"(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)" : - R"(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)", - get(get_arg(0)), - get(get_arg(1)), - get(get_arg(2)), - get(get_arg(3)), - get(get_arg(4)), - get(get_arg(5)), - get(get_arg(6)), - get(get_arg(7)), - get(get_arg(8)), - get(get_arg(9)), - get(get_arg(10))); - } - - void write_guid_attribute(writer& w, TypeDef const& type) - { - auto fully_qualify_guid = (type.TypeNamespace() == "Windows.Foundation.Metadata"); - - w.write(R"([%("%")])", - fully_qualify_guid ? "global::System.Runtime.InteropServices.Guid" : "Guid", - bind(type, false)); - } - - void write_guid_bytes(writer& w, TypeDef const& type) - { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); - } - - auto args = attribute.Value().FixedArgs(); - - using std::get; - - auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - - w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", - (get(get_arg(0)) >> 0) & 0xFF, - (get(get_arg(0)) >> 8) & 0xFF, - (get(get_arg(0)) >> 16) & 0xFF, - (get(get_arg(0)) >> 24) & 0xFF, - (get(get_arg(1)) >> 0) & 0xFF, - (get(get_arg(1)) >> 8) & 0xFF, - (get(get_arg(2)) >> 0) & 0xFF, - (get(get_arg(2)) >> 8) & 0xFF, - get(get_arg(3)), - get(get_arg(4)), - get(get_arg(5)), - get(get_arg(6)), - get(get_arg(7)), - get(get_arg(8)), - get(get_arg(9)), - get(get_arg(10))); - } - - static void write_iid_guid_property_name(writer& w, TypeDef const& type) - { - std::string name = w.write_temp("%", bind(type, typedef_name_type::ABI, true)); - name = escape_type_name_for_identifier(name, true, true); - w.write("IID_%", name); - } - - static void write_iid_guid_interop_property_name(writer& w, TypeDef const& type) - { - w.write("IID_%", bind(type, typedef_name_type::InteropIID)); - } - - static void write_iid_reference_guid_property_name(writer& w, TypeDef const& type) - { - std::string name = w.write_temp("%", bind(type, typedef_name_type::ABI, true)); - name = escape_type_name_for_identifier(name, true, true); - w.write("IID_%Reference", name); - } - - static void write_iid_reference_guid(writer& w, TypeDef const& type) - { - w.write("global::ABI.InterfaceIIDs.%", bind(type)); - } - - static void write_iid_guid(writer& w, TypeDef const& type) - { - if (distance(type.GenericParam()) != 0) - { - write_iid_guid_property_name(w, type); - // This is a unsafe accessor call, so write the function call to it too. - w.write("(null)"); - return; - } - - if (auto mapping = get_mapped_type(type.TypeNamespace(), type.TypeName())) - { - if (mapping->mapped_name == "IStringable") - { - w.write("global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IStringable"); - return; - } - - std::string name = w.write_temp("%", bind(type, typedef_name_type::NonProjected, true)); - name = escape_type_name_for_identifier(name, true, true); - w.write("global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_%", name); - } - else - { - w.write("global::ABI.InterfaceIIDs.%", bind(type)); - } - } - - static void write_iid_guid_with_type_semantics(writer& w, type_semantics const& semantics) - { - for_typedef(w, semantics, [&](auto type) - { - write_iid_guid(w, type); - }); - } - - static void write_iid_guid_property_from_type(writer& w, TypeDef const& type) - { - w.write( - R"(public static ref readonly Guid % -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - % - ]; - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } -} - -)", bind(type), bind(type)); - } - - static void write_iid_guid_property_from_signature(writer& w, TypeDef const& type) - { - std::string guid_sig = w.write_temp("%", bind(type)); - std::string ireference_guid_sig = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};" + guid_sig + ")"; - GUID guid_value = generate_guid(ireference_guid_sig); - - w.write( - R"(public static ref readonly Guid % -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - )", bind(type)); - - w.write_printf( - "0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X\n", - (guid_value.Data1 >> 0) & 0xFF, (guid_value.Data1 >> 8) & 0xFF, (guid_value.Data1 >> 16) & 0xFF, (guid_value.Data1 >> 24) & 0xFF, - (guid_value.Data2 >> 0) & 0xFF, (guid_value.Data2 >> 8) & 0xFF, - (guid_value.Data3 >> 0) & 0xFF, (guid_value.Data3 >> 8) & 0xFF, - guid_value.Data4[0], - guid_value.Data4[1], - guid_value.Data4[2], - guid_value.Data4[3], - guid_value.Data4[4], - guid_value.Data4[5], - guid_value.Data4[6], - guid_value.Data4[7]); - - w.write(R"( ]; - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } -} - -)"); - } - - static void write_iid_guid_property_for_class_interfaces(writer& w, TypeDef const& type, std::set& interfacesEmitted) - { - for (auto& iface : type.InterfaceImpl()) - { - auto semantics = get_type_semantics(iface.Interface()); - for_typedef(w, semantics, [&](TypeDef ifaceType) - { - // IIDs for custom mapped interfaces and generic interfaces are handled differently. - if (auto mapping = get_mapped_type(ifaceType.TypeNamespace(), ifaceType.TypeName())) - { - return; - } - - if (size(ifaceType.GenericParam()) != 0) - { - return; - } - - if (interfacesEmitted.find(ifaceType) != interfacesEmitted.end()) - { - return; - } - - // If the IID for the interface is not already going to be written due to - // being projected in this projection, then write it so that we can use it - // for our objref. - if (!settings.filter.includes(ifaceType)) - { - write_iid_guid_property_from_type(w, ifaceType); - interfacesEmitted.insert(ifaceType); - } - }); - } - } - - void write_event_source_type_name(writer& w, type_semantics const& eventTypeSemantics) - { - w.write("%Source", bind(eventTypeSemantics, typedef_name_type::ABI, false)); - } - - MethodDef get_event_invoke_method(TypeDef const& eventType) - { - for (auto&& method : eventType.MethodList()) - { - if (method.Name() == "Invoke") - { - return method; - } - } - throw_invalid("Event type must have an Invoke method"); - } - - method_signature get_event_invoke_method_signature(TypeDef const& eventType) - { - return method_signature(get_event_invoke_method(eventType)); - } - - void write_event_invoke_params(writer& w, method_signature const& methodSig) - { - w.write("%", bind_list(", ", methodSig.params())); - } - - void write_event_invoke_return(writer& w, method_signature const& methodSig) - { - if (methodSig.return_signature()) - { - w.write("return "); - } - } - - void write_event_invoke_return_default(writer& w, method_signature const& methodSig) - { - if (!methodSig.return_signature()) - { - return; - } - auto&& semantics = get_type_semantics(methodSig.return_signature().Type()); - w.write("default(%)", bind(semantics)); - } - - void write_event_out_defaults(writer& w, method_signature const& methodSig) - { - for (auto&& param : methodSig.params()) - { - if (get_param_category(param) == param_category::out || get_param_category(param) == param_category::receive_array) - { - w.write("\n% = default(%);", bind(param), bind(get_type_semantics(param.second->Type()))); - } - } - } - - void write_event_invoke_args(writer& w, method_signature const& methodSig) - { - w.write("%", bind_list(", ", methodSig.params())); - } - - void write_abi_type(writer& w, type_semantics const& semantics) - { - call(semantics, - [&](object_type) { w.write("void*"); }, - [&](guid_type) { w.write("Guid"); }, - [&](type_type) { throw_invalid("System.Type not implemented"); }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::enum_type: - write_type_name(w, type); - break; - - case category::struct_type: - write_type_name(w, type, !is_type_blittable(semantics) ? typedef_name_type::ABI : typedef_name_type::Projected); - break; - - default: - w.write("void*"); - break; - }; - }, - [&](generic_type_index const& var) - { - write_generic_type_name(w, var.index); - }, - [&](generic_type_instance const&) - { - w.write("void*"); - }, - [&](generic_type_param const& param) - { - w.write(param.Name()); - }, - [&](fundamental_type type) - { - if (type == fundamental_type::String) - { - w.write("void*"); - } - else - { - write_fundamental_type(w, type); - } - }); - } - - void write_abi_parameter(writer& w, method_signature::param_t const& param) - { - auto semantics = get_type_semantics(param.second->Type()); - auto param_name = w.write_temp("%", bind(param)); - switch (get_param_category(param)) - { - case param_category::in: - w.write(", % %", bind(semantics), param_name); - break; - case param_category::ref: - w.write(", %* %", bind(semantics), param_name); - break; - case param_category::out: - w.write(", %* %", bind(semantics), param_name); - break; - case param_category::pass_array: - case param_category::fill_array: - w.write(", uint __%Size, void* %", param_name, param_name); - break; - case param_category::receive_array: - w.write(", uint* __%Size, %** %", param_name, bind(semantics), param_name); - break; - } - } - - void write_abi_parameter_type(writer& w, method_signature::param_t const& param) - { - auto semantics = get_type_semantics(param.second->Type()); - switch (get_param_category(param)) - { - case param_category::in: - w.write(", %", bind(semantics)); - break; - case param_category::ref: - w.write(", in %", bind(semantics)); - break; - case param_category::out: - w.write(", out %", bind(semantics)); - break; - case param_category::pass_array: - case param_category::fill_array: - w.write(", uint, IntPtr"); - break; - case param_category::receive_array: - w.write(", out uint, out IntPtr"); - break; - } - } - - void write_abi_parameter_type_pointer(writer& w, method_signature::param_t const& param) - { - auto semantics = get_type_semantics(param.second->Type()); - switch (get_param_category(param)) - { - case param_category::in: - w.write(", %", bind(semantics)); - break; - case param_category::ref: - w.write(", %*", bind(semantics)); - break; - case param_category::out: - w.write(", %*", bind(semantics)); - break; - case param_category::pass_array: - case param_category::fill_array: - w.write(", uint, void*"); - break; - case param_category::receive_array: - w.write(", uint*, %**", bind(semantics)); - break; - } - } - - void write_abi_return(writer& w, method_signature const& signature) - { - if (auto return_sig = signature.return_signature()) - { - auto semantics = get_type_semantics(return_sig.Type()); - auto return_param = w.write_temp("%", bind(signature.return_param_name())); - return_sig.Type().is_szarray() ? - w.write(", uint* __%Size, %** %", signature.return_param_name(), bind(semantics), return_param) : - w.write(", %* %", bind(semantics), return_param); - } - } - - void write_abi_return_type(writer& w, method_signature const& signature) - { - if (auto return_sig = signature.return_signature()) - { - auto semantics = get_type_semantics(return_sig.Type()); - return_sig.Type().is_szarray() ? - w.write(", out uint, out IntPtr") : - w.write(", out %", bind(semantics)); - } - } - - void write_abi_return_type_pointer(writer& w, method_signature const& signature) - { - if (auto return_sig = signature.return_signature()) - { - auto semantics = get_type_semantics(return_sig.Type()); - return_sig.Type().is_szarray() ? - w.write(", uint*, %**", bind(semantics)) : - w.write(", %*", bind(semantics)); - } - } - - void write_abi_parameters(writer& w, method_signature const& signature) - { - w.write("void* thisPtr"); - for (auto&& param : signature.params()) - { - write_abi_parameter(w, param); - } - write_abi_return(w, signature); - } - - void write_abi_parameters_without_return(writer& w, method_signature const& signature) - { - w.write("IntPtr thisPtr"); - for (auto&& param : signature.params()) - { - write_abi_parameter(w, param); - } - } - - void write_abi_parameter_types(writer& w, method_signature const& signature) - { - w.write("IntPtr"); - for (auto&& param : signature.params()) - { - write_abi_parameter_type(w, param); - } - write_abi_return_type(w, signature); - } - - void write_abi_parameter_types_without_return(writer& w, method_signature const& signature) - { - w.write("IntPtr"); - for (auto&& param : signature.params()) - { - write_abi_parameter_type(w, param); - } - } - - void write_abi_parameter_types_pointer(writer& w, method_signature const& signature) - { - w.write("void*"); - for (auto&& param : signature.params()) - { - write_abi_parameter_type_pointer(w, param); - } - write_abi_return_type_pointer(w, signature); - } - - void write_abi_parameter_types_pointer_without_return(writer& w, method_signature const& signature) - { - w.write("void*"); - for (auto&& param : signature.params()) - { - write_abi_parameter_type_pointer(w, param); - } - } - - void write_abi_delegate_parameter_types_pointer(writer& w, MethodDef const& method) - { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); - w.write("Abi"); - }); - - w.write("delegate* unmanaged[Stdcall]<%, int>", - bind(method_signature(method))); - } - - bool abi_signature_has_generic_parameters(writer& w, method_signature const& signature) - { - bool signature_has_generic_parameters{}; - - writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { - signature_has_generic_parameters = true; - }); - - auto _ = w.write_temp("%", bind(signature)); - return signature_has_generic_parameters; - } - - bool abi_signature_without_return_has_generic_parameters(writer& w, method_signature const& signature) - { - bool signature_has_generic_parameters{}; - - writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { - signature_has_generic_parameters = true; - }); - - auto _ = w.write_temp("%", bind(signature)); - return signature_has_generic_parameters; - } - - bool projected_signature_has_generic_parameters(writer& w, method_signature const& signature) - { - bool signature_has_generic_parameters{}; - - writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { - signature_has_generic_parameters = true; - }); - - auto _ = w.write_temp("%%", - bind_list(", ", signature.params()), - bind(signature)); - return signature_has_generic_parameters; - } - - template - void write_event_params(writer& w, row_base::value_type const& evt, write_params params) - { - method_signature add_sig{ std::get<0>(get_event_methods(evt)) }; - auto semantics = get_type_semantics(add_sig.params().at(0).second->Type()); - - if (auto td = std::get_if(&semantics)) - { - method_signature invoke_sig{ get_delegate_invoke(*td) }; - if (invoke_sig.params().size() > 0) - { - params(w, invoke_sig); - } - } - else if (auto gti = std::get_if(&semantics)) - { - auto guard{ w.push_generic_args(*gti) }; - method_signature invoke_sig{ get_delegate_invoke(gti->generic_type) }; - params(w, invoke_sig); - } - } - - void write_event_param_types(writer& w, row_base::value_type const& evt) - { - auto write_params = [](writer& w, method_signature const& invoke_sig) - { - w.write("<%>", bind_list(", ", invoke_sig.params())); - }; - write_event_params(w, evt, write_params); - } - - void write_delegate_abi_call(writer& w, TypeDef const& type, std::string_view call, std::string_view name) - { - w.write("%%.%(%)", - bind(type, typedef_name_type::ABI, false), - bind(type), - call, name); - } - - void write_object_marshal_from_abi(writer& w, type_semantics const& param_type, TypeDef const& type, std::string_view name, bool is_boxed = false) - { - switch (get_category(type)) - { - case category::enum_type: - { - if (is_boxed) - { - w.write("(%)", bind(type, typedef_name_type::Projected, false)); - } - w.write("%", name); - return; - } - case category::delegate_type: - { - write_delegate_abi_call(w, type, "FromAbi", name); - return; - } - case category::struct_type: - { - if (is_type_blittable(param_type)) - { - w.write("%", name); - } - else - { - w.write("%.FromAbi(%)", bind(param_type, typedef_name_type::ABI, true), name); - } - return; - } - case category::interface_type: - { - w.write("MarshalInterface<%>.FromAbi(%)", - bind(type, typedef_name_type::Projected, false), - name); - return; - } - case category::class_type: - { - w.write("%.FromAbi(%)", - bind(param_type), - name); - return; - } - } - } - - void write_fundamental_marshal_to_abi(writer& w, fundamental_type type, std::string_view name) - { - switch (type) - { - case fundamental_type::String: - w.write("%.Handle", name); - break; - default: - w.write("%", name); - break; - } - } - - void write_fundamental_marshal_from_abi(writer& w, fundamental_type type, std::string_view name, bool is_boxed = false) - { - if (type == fundamental_type::String) - { - w.write(R"(MarshalString.FromAbi(%))", name); - } - else if (is_boxed) - { - w.write("(%)(object)%", bind(type), name); - } - else - { - w.write("%", name); - } - } - - void write_class_modifiers(writer& w, TypeDef const& type) - { - if (is_static(type)) - { - w.write("static "); - return; - } - - if (type.Flags().Sealed()) - { - w.write("sealed "); - } - } - - void write_objref_type_name(writer& w, type_semantics const& ifaceTypeSemantics); - - bool is_manually_generated_iface(TypeDef const& ifaceType); - - void write_abi_static_method_call(writer& w, type_semantics const& iface, MethodDef const& method, std::string const& targetObjRef) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - method_signature signature{ method }; - w.write("%.%(%%%)", bind(iface, typedef_name_type::StaticAbiClass, true), - method.Name(), - targetObjRef, - signature.has_params() ? ", " : "", - bind_list(", ", signature.params())); - } - - void write_unsafe_accessor_static_method_call(writer& w, std::string const& unsafeAccessorMethod, MethodDef const& method, std::string const& targetObjRef) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - method_signature signature{ method }; - w.write("%(null, %%%)", - unsafeAccessorMethod, - targetObjRef, - signature.has_params() ? ", " : "", - bind_list(", ", signature.params())); - } - - void write_abi_get_property_static_method_call(writer& w, type_semantics const& iface, Property const& prop, std::string const& targetObjRef) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - w.write("%.%(%)", - bind(iface, typedef_name_type::StaticAbiClass, true), - prop.Name(), - targetObjRef); - } - - void write_abi_set_property_static_method_call(writer& w, type_semantics const& iface, Property const& prop, std::string const& targetObjRef) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - w.write("%.%(%, value)", - bind(iface, typedef_name_type::StaticAbiClass, true), - prop.Name(), - targetObjRef); - } - - void write_unsafe_accessor_property_static_method_call(writer& w, std::string const& unsafeAccessorMethod, std::string const& targetObjRef, bool get) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - w.write("%(null, %%)", - unsafeAccessorMethod, - targetObjRef, - get ? "" : ", value"); - } - - void write_abi_event_source_static_method_call(writer& w, type_semantics const& iface, Event const& evt, bool isSubscribeCall, std::string const& targetObjRef, bool is_static_event = false) - { - if (settings.reference_projection) - { - w.write("throw null"); - return; - } - - bool is_unsafe_accessor_call = false; - w.write("%(%, %).%(value)", - bind([&](writer& w) { - for_typedef(w, iface, [&](type_definition const& type) - { - if (size(type.GenericParam()) != 0) - { - is_unsafe_accessor_call = true; - auto interface_name = w.write_temp("%", bind(type, typedef_name_type::Projected, true)); - w.write("%_%", escape_type_name_for_identifier(interface_name, true), evt.Name()); - } - else - { - w.write("%.%", bind(type, typedef_name_type::StaticAbiClass, true), evt.Name()); - } - }); - }), - bind([&](writer& w) { - if (is_unsafe_accessor_call) - { - w.write("null, "); - } - - if (is_static_event) - { - w.write("%", targetObjRef); - } - else - { - w.write("(WindowsRuntimeObject)this"); - } - }), - targetObjRef, - isSubscribeCall ? "Subscribe" : "Unsubscribe"); - } - - void write_method(writer& w, method_signature signature, std::string_view method_name, - std::string_view return_type, std::string_view method_target, - std::string_view access_spec = ""sv, std::string_view method_spec = ""sv, - std::string_view platform_attribute = ""sv, - std::optional> paramsForStaticMethodCall = {}) - { - std::optional unsafe_accessor; - if (paramsForStaticMethodCall.has_value()) - { - auto parent = paramsForStaticMethodCall.value().second.Parent(); - if (size(parent.GenericParam()) != 0) - { - auto parent_type_name = w.write_temp("%", bind(parent, typedef_name_type::Projected, true)); - unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), method_name); - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %([UnsafeAccessorType("%, WinRT.Interop")] object _, WindowsRuntimeObjectReference thisReference%%); -)", - method_name, - return_type, - unsafe_accessor.value(), - bind(parent, typedef_name_type::StaticAbiClass), - signature.params().size() != 0 ? ", " : "", - bind_list(", ", signature.params())); - } - } - - w.write(R"( -%%%% %(%) => %; -)", - platform_attribute, - access_spec, - method_spec, - return_type, - method_name, - bind_list(", ", signature.params()), - bind([&](writer& w) { - if (paramsForStaticMethodCall.has_value()) - { - if (unsafe_accessor.has_value()) - { - w.write("%", bind( - unsafe_accessor.value(), - paramsForStaticMethodCall.value().second, - w.write_temp("%", method_target))); - } - else - { - w.write("%", bind( - paramsForStaticMethodCall.value().first, - paramsForStaticMethodCall.value().second, - w.write_temp("%", method_target))); - } - } - else - { - w.write("%.%(%)", method_target, - method_name, - bind_list(", ", signature.params())); - } - })); - } - - void write_explicitly_implemented_method_for_abi(writer& w, MethodDef const& method, - std::string_view return_type, TypeDef const& method_interface, std::string_view method_target) - { - // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. - bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(method_interface); - - method_signature signature{ method }; - w.write(R"( -% %.%(%) => %.%(%); -)", - return_type, - bind(method_interface, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false), - method.Name(), - bind_list(", ", signature.params()), - method_target, - method.Name(), - bind_list(", ", signature.params()) - ); - } - - auto method_signature_equal(writer& w, MethodDef const& first, MethodDef const& second) - { - method_signature signature_first{ first }; - method_signature signature_second{ second }; - - if (size(signature_first.params()) != size(signature_second.params())) - { - return false; - } - - auto first_method_return_type = w.write_temp("%", bind(signature_first)); - auto second_method_return_type = w.write_temp("%", bind(signature_second)); - if (first_method_return_type != second_method_return_type) - { - return false; - } - - auto first_method_parameters = w.write_temp("%", bind_list(", ", signature_first.params())); - auto second_method_parameters = w.write_temp("%", bind_list(", ", signature_second.params())); - return first_method_parameters == second_method_parameters; - } - - void write_non_projected_type(writer& w, TypeDef const& type) - { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::NonProjected); - }); - - w.write("%", bind(type, typedef_name_type::NonProjected, false)); - } - - auto is_implemented_as_private_method(writer& w, TypeDef const& class_type, MethodDef const& interface_method) - { - auto interface_method_name = w.write_temp( - "%.%", - bind(interface_method.Parent()), - interface_method.Name()); - for (auto&& class_method : class_type.MethodList()) - { - if (class_method.Flags().Access() == MemberAccess::Private && - class_method.Name() == interface_method_name && - method_signature_equal(w, class_method, interface_method)) - { - return true; - } - } - - return false; - } - - auto is_implemented_as_private_mapped_interface(writer& w, TypeDef const& class_type, TypeDef const& interface_type) - { - // Assume as long as one member of the custom mapped interface is implemented as a private member, - // that the entire interface is implemented as an explicit implementation. - if (size(interface_type.MethodList()) != 0) - { - return is_implemented_as_private_method(w, class_type, interface_type.MethodList().first); - } - - if (size(interface_type.PropertyList()) != 0) - { - auto [getter, _] = get_property_methods(interface_type.PropertyList().first); - return is_implemented_as_private_method(w, class_type, getter); - } - - if (size(interface_type.EventList()) != 0) - { - auto [add, _] = get_event_methods(interface_type.EventList().first); - return is_implemented_as_private_method(w, class_type, add); - } - - return false; - } - - void write_class_method( - writer& w, - MethodDef const& method, - TypeDef const& class_type, - bool is_overridable, - bool is_protected, - std::string_view platform_attribute, - type_semantics static_method_semantics) - { - if (method.SpecialName()) - { - return; - } - - auto access_spec = is_protected || is_overridable ? "protected " : "public "; - std::string method_spec = ""; - - // If this interface is overridable but the type is sealed, don't mark the member as virtual. - // The C# compiler errors out about declaring a virtual member in a sealed class. - if (is_overridable && !class_type.Flags().Sealed()) - { - // All overridable methods in the WinRT type system have protected visibility. - access_spec = "protected "; - method_spec = "virtual "; - } - - method_signature signature{ method }; - auto raw_return_type = w.write_temp("%", [&](writer& w) { - write_projection_return_type(w, signature); - }); - auto return_type = raw_return_type; - if (method.Name() == "ToString") - { - method_spec += "new "; - if (signature.params().empty()) - { - if (auto ret = signature.return_signature()) - { - auto semantics = get_type_semantics(ret.Type()); - if (auto ft = std::get_if(&semantics)) - { - if (*ft == fundamental_type::String) - { - method_spec = "override "; - return_type = "string"; - } - } - } - } - } - - bool method_return_matches; - if (is_object_equals_method(method, &method_return_matches) || - is_object_hashcode_method(method, &method_return_matches)) - { - if (method_return_matches) - { - method_spec = "override "; - } - else - { - method_spec += "new "; - } - } - - bool is_private = is_implemented_as_private_method(w, class_type, method); - auto static_method_params = std::pair(static_method_semantics, method); - if (!is_private) - { - write_method(w, signature, method.Name(), return_type, - w.write_temp("%", bind(static_method_params.first)), - access_spec, method_spec, platform_attribute, std::optional(static_method_params)); - } - - // If overridable or private, we need to generate the explcit method - if (is_overridable || is_private) - { - w.write(R"( -%% %.%(%) => %;)", - platform_attribute, - bind(signature), - bind(method.Parent(), typedef_name_type::CCW, false), - method.Name(), - bind_list(", ", signature.params()), - bind([&](writer& w) - { - if (is_private) - { - w.write("%", bind(static_method_params.first, static_method_params.second, - w.write_temp("%", bind(static_method_params.first)))); - } - else - { - w.write(method.Name()); - w.write("(%)", bind_list(", ", signature.params())); - } - })); - } - } - - void write_property(writer& w, std::string_view external_prop_name, std::string_view prop_name, - std::string_view prop_type, std::string_view getter_target, std::string_view setter_target, - std::string_view access_spec = ""sv, std::string_view method_spec = ""sv, - std::string_view getter_platform = ""sv, std::string_view setter_platform = ""sv, - std::optional> const& params_for_static_getter = {}, std::optional> const& params_for_static_setter = {}) - { - if (setter_target.empty() && !params_for_static_setter.has_value()) - { - w.write(R"( -%%%% % => %; -)", - getter_platform, - access_spec, - method_spec, - prop_type, - external_prop_name, - bind([&](writer& w) { - if (params_for_static_getter.has_value()) - { - w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", getter_target))); - } - else - { - w.write("%.%", getter_target, prop_name); - } - })); - return; - } - - std::string_view property_platform = ""sv; - if (getter_platform == setter_platform) - { - property_platform = getter_platform; - getter_platform = ""sv; - setter_platform = ""sv; - } - - std::optional unsafe_accessor; - if (params_for_static_getter.has_value()) - { - auto parent = params_for_static_getter.value().second.Parent(); - if (size(parent.GenericParam()) != 0) - { - auto parent_type_name = w.write_temp("%", bind(params_for_static_getter.value().first, typedef_name_type::Projected, true)); - unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), external_prop_name); - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %([UnsafeAccessorType("%, WinRT.Interop")] object _, WindowsRuntimeObjectReference thisReference); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern void %([UnsafeAccessorType("%, WinRT.Interop")] object _, WindowsRuntimeObjectReference thisReference, % value); -)", - // get - external_prop_name, - prop_type, - unsafe_accessor.value(), - bind(params_for_static_getter.value().first, typedef_name_type::StaticAbiClass), - // set - external_prop_name, - unsafe_accessor.value(), - bind(params_for_static_getter.value().first, typedef_name_type::StaticAbiClass), - prop_type); - } - } - - w.write(R"( -%%%% % -{ -%get => %; -%set => %; -} -)", - property_platform, - access_spec, - method_spec, - prop_type, - external_prop_name, - getter_platform, - bind([&](writer& w) { - if (params_for_static_getter.has_value()) - { - if (unsafe_accessor.has_value()) - { - w.write("%", bind( - unsafe_accessor.value(), - w.write_temp("%", getter_target), - true)); - } - else - { - w.write("%", bind( - params_for_static_getter.value().first, - params_for_static_getter.value().second, - w.write_temp("%", getter_target))); - } - } - else - { - w.write("%.%", getter_target, prop_name); - } - }), - setter_platform, - bind([&](writer& w) { - if (params_for_static_setter.has_value()) - { - if (unsafe_accessor.has_value()) - { - w.write("%", bind( - unsafe_accessor.value(), - w.write_temp("%", setter_target), - false)); - } - else - { - w.write("%", bind( - params_for_static_setter.value().first, - params_for_static_setter.value().second, - w.write_temp("%", setter_target))); - } - } - else - { - w.write("%.% = value", setter_target, prop_name); - } - })); - } - - std::string write_as_cast(writer& w, TypeDef const& iface) - { - return w.write_temp("((%)(WindowsRuntimeObject)this)", bind(iface, typedef_name_type::Projected, false)); - } - - std::string write_explicit_name(writer& w, TypeDef const& iface, std::string_view name) - { - // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. - bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(iface); - - return w.write_temp("%.%", write_type_name_temp(w, iface, "%", implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected), name); - } - - std::string write_prop_type(writer& w, Property const& prop, bool is_set_property = false) - { - return w.write_temp("%", bind(prop.Type().Type(), is_set_property)); - } - - void write_explicitly_implemented_property_for_abi(writer& w, Property const& prop, TypeDef const& iface) - { - auto prop_target = write_as_cast(w, iface); - auto [getter, setter] = get_property_methods(prop); - auto getter_target = getter ? prop_target : ""; - auto setter_target = setter ? prop_target : ""; - write_property(w, write_explicit_name(w, iface, prop.Name()), prop.Name(), - write_prop_type(w, prop), getter_target, setter_target); - } - - void write_event(writer& w, std::string_view external_event_name, Event const& event, std::string_view event_target, - std::string_view access_spec = ""sv, std::string_view method_spec = ""sv, std::string_view platform_attribute = ""sv, - std::optional> paramsForStaticMethodCall = {}, - std::string_view inline_event_source_field = ""sv) - { - auto event_type = w.write_temp("%", bind(get_type_semantics(event.EventType()), typedef_name_type::Projected, false)); - auto parent_type = event.Parent(); - - // Microsoft.UI.Xaml.Input.ICommand has a lower-fidelity type mapping where the type of the event handler doesn't project one-to-one - // so we need to hard-code mapping the event handler from the mapped WinRT type to the correct .NET type. - if (event.Name() == "CanExecuteChanged" && event_type == "global::System.EventHandler") - { - auto parent_type_name = w.write_temp("%", bind(parent_type, typedef_name_type::NonProjected, true)); - if (parent_type_name == "Microsoft.UI.Xaml.Input.ICommand" || parent_type_name == "Windows.UI.Xaml.Input.ICommand") - { - event_type = "global::System.EventHandler"; - } - } - - if (paramsForStaticMethodCall.has_value() && size(parent_type.GenericParam()) != 0) - { - auto parent_type_name = w.write_temp("%", bind(parent_type, typedef_name_type::Projected, true)); - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %_%([UnsafeAccessorType("%, WinRT.Interop")] object _, object thisObject, WindowsRuntimeObjectReference thisReference); -)", - external_event_name, - bind(get_type_semantics(event.EventType()), typedef_name_type::EventSource, false), - escape_type_name_for_identifier(parent_type_name, true), - external_event_name, - bind(parent_type, typedef_name_type::StaticAbiClass)); - } - - w.write(R"( -%%%event % % -{ -add => %; -remove => %; -} -)", - platform_attribute, - access_spec, - method_spec, - event_type, - external_event_name, - bind([&](writer& w) { - if (!inline_event_source_field.empty()) - { - if (settings.reference_projection) - { - w.write("throw null"); - } - else - { - w.write("_eventSource_%.Subscribe(value)", inline_event_source_field); - } - } - else if (paramsForStaticMethodCall.has_value()) - { - auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); - w.write("%", bind(iface_type_semantics, event, true, - w.write_temp("%", event_target), is_static)); - } - else - { - w.write("%.% += value", event_target, event.Name()); - } - }), - bind([&](writer& w) { - if (!inline_event_source_field.empty()) - { - if (settings.reference_projection) - { - w.write("throw null"); - } - else - { - w.write("_eventSource_%.Unsubscribe(value)", inline_event_source_field); - } - } - else if (paramsForStaticMethodCall.has_value()) - { - auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); - w.write("%", bind(iface_type_semantics, event, false, - w.write_temp("%", event_target), is_static)); - } - else - { - w.write("%.% -= value", event_target, event.Name()); - } - })); - } - - void write_explicitly_implemented_event_for_abi(writer& w, Event const& evt, TypeDef const& iface) - { - write_event(w, write_explicit_name(w, iface, evt.Name()), evt, write_as_cast(w, iface)); - } - - void write_class_event(writer& w, Event const& event, TypeDef const& class_type, bool is_overridable, bool is_protected, std::string_view interface_member, std::string_view platform_attribute, type_semantics static_method_semantics, bool use_inline_event_source) - { - auto visibility = "public "; - - if (is_protected) - { - visibility = "protected "; - } - - if (is_overridable) - { - visibility = "protected virtual "; - } - - auto [add, _] = get_event_methods(event); - bool is_private = is_implemented_as_private_method(w, class_type, add); - - if (!is_private) - { - if (use_inline_event_source) - { - write_event(w, event.Name(), event, ""sv, visibility, ""sv, platform_attribute, std::nullopt, event.Name()); - } - else - { - auto event_target = w.write_temp("%", bind(static_method_semantics)); - write_event(w, event.Name(), event, event_target, visibility, ""sv, platform_attribute, - std::optional(std::tuple(static_method_semantics, event, false))); - } - } - - // If overridable or private, we need to generate the explicit event - if (is_overridable || is_private) - { - auto explicit_event_name = w.write_temp("%.%", bind(event.Parent(), typedef_name_type::CCW, false), event.Name()); - - if (is_private) - { - if (use_inline_event_source) - { - auto interface_name = w.write_temp("%", bind(event.Parent(), typedef_name_type::CCW, false)); - auto event_source_field_name = escape_type_name_for_identifier(interface_name, true) + "_" + std::string(event.Name()); - - write_event(w, explicit_event_name, event, ""sv, ""sv, ""sv, platform_attribute, std::nullopt, event_source_field_name); - } - else - { - write_event(w, explicit_event_name, event, interface_member, ""sv, ""sv, platform_attribute, std::nullopt); - } - } - else - { - write_event(w, explicit_event_name, event, "this", ""sv, ""sv, platform_attribute, std::nullopt); - } - } - } - - auto write_custom_attribute_args(writer& w, CustomAttribute const& attribute, CustomAttributeSig const& signature) - { - auto write_fixed_arg = [&](writer & w, FixedArgSig arg) - { - if (std::holds_alternative>(arg.value)) - { - throw_invalid("ElemSig list unexpected"); - } - auto&& arg_value = std::get(arg.value); - - call(arg_value.value, - [&](ElemSig::SystemType system_type) - { - auto arg_type = attribute.get_cache().find_required(system_type.name); - if (is_static(arg_type)) - { - w.write("typeof(%)", bind(arg_type)); - } - else - { - w.write("typeof(%)", bind(arg_type)); - } - }, - [&](ElemSig::EnumValue enum_value) - { - if (enum_value.type.m_typedef.TypeName() == "AttributeTargets") - { - std::vector values; - auto value = std::get(enum_value.value); - if (value == 4294967295) - { - values.emplace_back("All"); - } - else - { - static struct - { - uint32_t value; - char const* name; - } - attribute_target_enums[] = - { - { 1, "Delegate" }, - { 2, "Enum" }, - { 4, "Event" }, - { 8, "Field" }, - { 16, "Interface" }, - { 64, "Method" }, - { 128, "Parameter" }, - { 256, "Property" }, - { 512, "Class" }, // "RuntimeClass" - { 1024, "Struct" }, - { 2048, "All" }, // "InterfaceImpl" - { 8192, "Struct" }, // "ApiContract" - }; - for (auto&& target_enum : attribute_target_enums) - { - if (value & target_enum.value) - { - values.emplace_back(target_enum.name); - } - } - } - w.write("%", - bind_list([](writer& w, auto&& value) { w.write("global::System.AttributeTargets.%", value); }, - " | ", values)); - } - else for (auto field : enum_value.type.m_typedef.FieldList()) - { - if (field.Name() == "value__") continue; - auto field_value = field.Constant().Value(); - if (std::visit([&](auto&& v) { return Constant::constant_type{ v } == field_value; }, enum_value.value)) - { - w.write("%.%", - bind(enum_value.type.m_typedef), - field.Name()); - } - } - }, - [&](std::string_view type_name) - { - bool previous_char_escape = false; - std::string sanitized_type_name; - sanitized_type_name.reserve(type_name.length() * 2); - for (const auto& c : type_name) - { - if (c == '\\' && !previous_char_escape) - { - previous_char_escape = true; - continue; - } - - // We only handle the following escape characters for now. - if (previous_char_escape && c != '\\' && c != '\'' && c != '"') - { - sanitized_type_name += '\\'; - } - previous_char_escape = false; - - sanitized_type_name += c; - if (c == '"') - { - sanitized_type_name += c; - } - } - w.write("^@\"%\"", sanitized_type_name); - }, - [&](auto&&) - { - if (auto uint32_value = std::get_if(&arg_value.value)) - { - w.write("%u", *uint32_value); - } - else if (auto int32_value = std::get_if(&arg_value.value)) - { - w.write(*int32_value); - } - else if (auto uint64_value = std::get_if(&arg_value.value)) - { - w.write(*uint64_value); - } - else if (auto int64_value = std::get_if(&arg_value.value)) - { - w.write(*int64_value); - } - else if (auto bool_value = std::get_if(&arg_value.value)) - { - w.write(*bool_value ? "true" : "false"); - } - else if (auto char_value = std::get_if(&arg_value.value)) - { - w.write(*char_value); - } - else if (auto uint8_value = std::get_if(&arg_value.value)) - { - w.write(*uint8_value); - } - else if (auto int8_value = std::get_if(&arg_value.value)) - { - w.write(*int8_value); - } - else if (auto uint16_value = std::get_if(&arg_value.value)) - { - w.write(*uint16_value); - } - else if (auto int16_value = std::get_if(&arg_value.value)) - { - w.write(*int16_value); - } - else if (auto float_value = std::get_if(&arg_value.value)) - { - w.write_printf("f", *float_value); - } - else if (auto double_value = std::get_if(&arg_value.value)) - { - w.write_printf("f", *double_value); - } - }); - }; - - std::vector params; - for (auto&& arg : signature.FixedArgs()) - { - params.push_back(w.write_temp("%", bind(write_fixed_arg, arg))); - } - for (auto&& arg : signature.NamedArgs()) - { - params.push_back(w.write_temp("% = %", arg.name, bind(write_fixed_arg, arg.value))); - } - - return params; - } - - std::string get_platform(writer& w, CustomAttributeSig const& signature, std::vector const& params) - { - auto& arg0 = signature.FixedArgs()[0]; - auto& elem = std::get(arg0.value); - std::string_view contract_name; - if (auto system_type = std::get_if(&elem.value)) - { - contract_name = system_type->name; - } - else - { - contract_name = std::get(elem.value); - } - auto contract_version = std::stoul(params[1]) >> 16; - auto& contract_platform = get_contract_platform(contract_name, contract_version); - if (contract_platform.empty()) - { - return {}; - } - auto platform = std::string(contract_platform); - if (w._check_platform) - { - if (platform <= w._platform) - { - return {}; - } - if (w._platform.empty()) - { - w._platform = platform; - } - } - return "\"Windows" + platform + "\""; - } - - std::string get_platform(writer& w, std::pair const& custom_attributes) - { - std::map> attributes; - for (auto&& attribute : custom_attributes) - { - auto [attribute_namespace, attribute_name] = attribute.TypeNamespaceAndName(); - attribute_name = attribute_name.substr(0, attribute_name.length() - "Attribute"sv.length()); - auto signature = attribute.Value(); - auto params = write_custom_attribute_args(w, attribute, signature); - // ContractVersion attribute ==> SupportedOSPlatform attribute - if (attribute_name == "ContractVersion" && signature.FixedArgs().size() == 2) - { - return get_platform(w, signature, params); - } - } - return {}; - } - - void write_platform_attribute(writer& w, std::pair const& custom_attributes) - { - if (!settings.reference_projection) - { - return; - } - auto platform = get_platform(w, custom_attributes); - if (platform.empty()) - { - return; - } - w.write("[global::System.Runtime.Versioning.SupportedOSPlatform(%)]\n", platform); - } - - std::string write_platform_attribute_temp(writer& w, TypeDef const& type) - { - return w.write_temp("%", bind(type.CustomAttribute())); - } - - void write_custom_attributes(writer& w, std::pair const& custom_attributes, bool enable_platform_attrib) - { - std::map> attributes; - bool allow_multiple = false; - for (auto&& attribute : custom_attributes) - { - auto [attribute_namespace, attribute_name] = attribute.TypeNamespaceAndName(); - attribute_name = attribute_name.substr(0, attribute_name.length() - "Attribute"sv.length()); - // GCPressure, Guid, Flags, ProjectionInternal are handled separately - if (attribute_name == "GCPressure" || attribute_name == "Guid" || - attribute_name == "Flags" || attribute_name == "ProjectionInternal") continue; - auto attribute_full = (attribute_name == "AttributeUsage") ? "System.AttributeUsage" : - w.write_temp("%.%", attribute_namespace, attribute_name); - auto signature = attribute.Value(); - auto params = write_custom_attribute_args(w, attribute, signature); - // ContractVersion attribute ==> SupportedOSPlatform attribute - if (settings.reference_projection && enable_platform_attrib && attribute_name == "ContractVersion" && signature.FixedArgs().size() == 2) - { - auto platform = get_platform(w, signature, params); - if (!platform.empty()) - { - attributes["System.Runtime.Versioning.SupportedOSPlatform"].push_back(platform); - } - } - // Skip metadata attributes without a projection - if (attribute_namespace == "Windows.Foundation.Metadata") - { - if (attribute_name == "AllowMultiple") - { - allow_multiple = true; - } - // ContractVersion is only emitted for reference assemblies - if (attribute_name == "ContractVersion") - { - if (!settings.reference_projection) - { - continue; - } - } - else if (attribute_name != "DefaultOverload" && attribute_name != "Overload" && - attribute_name != "AttributeUsage" && attribute_name != "Experimental") - { - continue; - } - } - attributes[attribute_full] = std::move(params); - } - if (auto&& usage = attributes.find("System.AttributeUsage"); usage != attributes.end()) - { - usage->second.push_back(w.write_temp("AllowMultiple = %", allow_multiple ? "true" : "false")); - } - - for (auto&& attribute : attributes) - { - w.write("[global::"); - w.write(attribute.first); - if (!attribute.second.empty()) - { - w.write("(%)", bind_list(", ", attribute.second)); - } - w.write("]\n"); - } - } - - void write_type_custom_attributes(writer& w, TypeDef const& type, bool enable_platform_attrib) - { - write_custom_attributes(w, type.CustomAttribute(), enable_platform_attrib); - } - - void write_constructor_callback_method_name(writer& w, MethodDef const& method) - { - w.write("%_%", method.Name(), size(method.Signature().Params())); - } - - void write_constructor_args_struct_name(writer& w, MethodDef const& method) - { - w.write("%_%Args", method.Name(), size(method.Signature().Params())); - } - - struct attributed_type - { - TypeDef type; - bool activatable{}; - bool statics{}; - bool composable{}; - bool visible{}; - }; - static auto get_attributed_types(writer& w, TypeDef const& type) - { - auto get_system_type = [&](auto&& signature) -> TypeDef - { - for (auto&& arg : signature.FixedArgs()) - { - if (auto type_param = std::get_if(&std::get(arg.value).value)) - { - return type.get_cache().find_required(type_param->name); - } - } - - return {}; - }; - - std::map result; - - for (auto&& attribute : type.CustomAttribute()) - { - auto attribute_name = attribute.TypeNamespaceAndName(); - - if (attribute_name.first != "Windows.Foundation.Metadata") - { - continue; - } - - auto signature = attribute.Value(); - attributed_type info; - - if (attribute_name.second == "ActivatableAttribute") - { - info.type = get_system_type(signature); - info.activatable = true; - } - else if (attribute_name.second == "StaticAttribute") - { - info.type = get_system_type(signature); - info.statics = true; - } - else if (attribute_name.second == "ComposableAttribute") - { - info.type = get_system_type(signature); - info.composable = true; - - for (auto&& arg : signature.FixedArgs()) - { - if (auto visibility = std::get_if(&std::get(arg.value).value)) - { - info.visible = std::get(visibility->value) == 2; - break; - } - } - } - else - { - continue; - } - - std::string name; - - if (info.type) - { - name = w.write_temp("%", info.type.TypeName()); - } - - result[name] = std::move(info); - } - - return result; - } - - void write_class_static_cache_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) - { - if (staticsType) - { - auto factory_class_name = settings.netstandard_compat ? - w.write_temp("BaseFactory<%.Vftbl>", bind(staticsType, typedef_name_type::ABI, true)) : - w.write_temp("BaseFactory"); - - auto statics_type_name = staticsType.TypeName(); - w.write(R"( -private static % _% = new %("%.%", %.IID); -)", - factory_class_name, - statics_type_name, - factory_class_name, - classType.TypeNamespace(), - classType.TypeName(), - bind(staticsType, typedef_name_type::StaticAbiClass, true)); - } - } - - void write_activation_factory_objref_definition(writer& w, TypeDef const& classType) - { - auto objrefname = w.write_temp("%", bind(classType)); - if (settings.reference_projection) - { - w.write(R"( -private static WindowsRuntimeObjectReference % -{ - get - { - throw null; - } -} -)", - objrefname); - return; - } - - - w.write(R"( -private static WindowsRuntimeObjectReference % -{ - get - { - var __% = field; - if (__% != null && __%.IsInCurrentContext) - { - return __%; - } - return field = WindowsRuntimeObjectReference.GetActivationFactory("%"); - } -} -)", - objrefname, - objrefname, - objrefname, - objrefname, - objrefname, - bind(classType, typedef_name_type::NonProjected, true)); - } - - void write_static_objref_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) - { - auto objrefname = w.write_temp("%", bind(staticsType)); - if (settings.reference_projection) - { - w.write(R"( -private static WindowsRuntimeObjectReference % -{ - get - { - throw null; - } -} -)", - objrefname); - return; - } - - - w.write(R"( -private static WindowsRuntimeObjectReference % -{ - get - { - var __% = field; - if (__% != null && __%.IsInCurrentContext) - { - return __%; - } - return field = WindowsRuntimeObjectReference.GetActivationFactory("%", %); - } -} -)", - objrefname, - objrefname, - objrefname, - objrefname, - objrefname, - bind(classType, typedef_name_type::NonProjected, true), - bind(staticsType)); - } - - template - void write_static_abi_class_raw(writer& w, TypeDef const& factory_type) - { - w.write(R"( -private static class _% -{%} -)", - bind(factory_type, typedef_name_type::StaticAbiClass, false), - bind_each([&](writer& w, MethodDef const& method) - { - method_writer(w, factory_type, method); - }, factory_type.MethodList())); - } - - void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method); - - void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method); - - static std::string get_default_interface_name(writer& w, TypeDef const& type, bool abiNamespace = true, bool forceCCW = false) - { - return w.write_temp("%", bind(get_type_semantics(get_default_interface(type)), abiNamespace ? typedef_name_type::ABI : forceCCW ? typedef_name_type::CCW : typedef_name_type::Projected, false)); - } - - void write_factory_constructors(writer& w, TypeDef const& factory_type, TypeDef const& class_type) - { - auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); - auto gc_pressure_amount = get_gc_pressure_amount(class_type); - auto marshaling_type = get_marshaling_type_name(class_type); - if (factory_type) - { - write_static_objref_definition(w, factory_type, class_type); - auto cache_object = w.write_temp("%", bind(factory_type)); - - auto platform_attribute = write_platform_attribute_temp(w, factory_type); - for (auto&& method : factory_type.MethodList()) - { - method_signature signature{ method }; - - w.write(R"( -%public unsafe %(%) - :base(%.Instance, %, %, %) -{ -%} -)", - platform_attribute, - // constructor name - class_type.TypeName(), - bind_list(", ", signature.params()), - // base - bind(method), - bind(default_type_semantics), - marshaling_type, - bind([&](writer& w) - { - if (signature.params().empty()) - { - w.write("default"); - } - else - { - w.write("WindowsRuntimeActivationArgsReference.CreateUnsafe(new %(%))", - bind(method), - bind_list(", ", signature.params())); - } - }), - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); - }); - - write_static_abi_method_with_raw_return_type(w, factory_type, method); - } - } - else - { - write_activation_factory_objref_definition(w, class_type); - auto objrefname = w.write_temp("%", bind(class_type)); - - w.write(R"( -public %() - :base(default(WindowsRuntimeActivationTypes.DerivedSealed), %, %, %) -{ -%} -)", - class_type.TypeName(), - objrefname, - bind(default_type_semantics), - marshaling_type, - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); - }); - } - } - - bool is_manually_generated_iface(TypeDef const& ifaceType) - { - if (ifaceType.TypeNamespace() == "Microsoft.UI.Xaml.Interop" && - (ifaceType.TypeName() == "IBindableVector" || ifaceType.TypeName() == "IBindableIterable")) - { - return true; - } - - return false; - } - - void write_objref_type_name(writer& w, type_semantics const& ifaceTypeSemantics) - { - auto objRefTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); - w.write("_objRef_%", escape_type_name_for_identifier(objRefTypeCode, true)); - } - - void write_unsafe_accessor_for_iid(writer& w, TypeDef ifaceType, bool isInNullableContext = false) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "get_%")] -static extern ref readonly Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop")] object% _); -)", - bind(ifaceType), - bind(ifaceType), - isInNullableContext ? "?" : ""); - } - - void write_class_objrefs_definition(writer& w, TypeDef const& classType, bool replaceDefaultByInner) - { - for (auto&& ii : classType.InterfaceImpl()) - { - auto semantics = get_type_semantics(ii.Interface()); - for_typedef(w, semantics, [&](TypeDef ifaceType) - { - if (is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && !is_default_interface(ii)) // fast abi non default interface - { - return; - } - - auto objrefname = bind(semantics); - bool isDefaultInterface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); - bool useInner = replaceDefaultByInner && isDefaultInterface; - - if (!useInner) - { - w.write(R"(% -private WindowsRuntimeObjectReference % -{ - get - { - [MethodImpl(MethodImplOptions.NoInlining)] - WindowsRuntimeObjectReference MakeObjectReference() - { - _ = global::System.Threading.Interlocked.CompareExchange( - location1: ref field, - value: NativeObjectReference.As(%), - comparand: null); - - return field; - } - - return field ?? MakeObjectReference(); - }% -} -)", - [&](writer& w) { - if (distance(ifaceType.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, ifaceType); - } - }, - objrefname, - bind(ifaceType), - [&](writer& w) { - if(isDefaultInterface) - { - w.write("\n init;"); - } - }); - - /* - TODO handle fast ABI - if (!classType.Flags().Sealed() && is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && is_default_interface(ii)) - { - w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultInterfaceObjRef(%), null);)", objrefname, get_class_hierarchy_index(classType)); - } - */ - } - else - { - w.write(R"(private WindowsRuntimeObjectReference % => NativeObjectReference;)", objrefname); - - // Write it here so that we can pass the default interface IID as part of the constructor. - if (distance(ifaceType.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, ifaceType); - } - } - }); - } - } - - void write_class_event_sources_definition(writer& w, TypeDef const& classType) - { - if (settings.reference_projection) - { - return; - } - - auto fast_abi_class_val = get_fast_abi_class_for_class(classType); - - for (auto&& ii : classType.InterfaceImpl()) - { - auto semantics = get_type_semantics(ii.Interface()); - for_typedef(w, semantics, [&](TypeDef ifaceType) - { - // Skip fast ABI exclusive non-default interfaces (same as objrefs) - if (is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && !is_default_interface(ii)) - { - return; - } - - // Skip custom mapped types (events handled by write_custom_mapped_type_members) - if (auto mapping = get_mapped_type(ifaceType.TypeNamespace(), ifaceType.TypeName()); mapping && mapping->has_custom_members_output) - { - return; - } - - auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(ifaceType); - auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(classType) : semantics; - auto objref_name = w.write_temp("%", bind(semantics_for_abi_call)); - - for (auto&& evt : ifaceType.EventList()) - { - auto [add, _] = get_event_methods(evt); - bool is_private = is_implemented_as_private_method(w, classType, add); - - std::string event_source_field_name; - if (is_private) - { - auto interface_name = w.write_temp("%", bind(ifaceType, typedef_name_type::CCW, false)); - event_source_field_name = escape_type_name_for_identifier(interface_name, true) + "_" + std::string(evt.Name()); - } - else - { - event_source_field_name = std::string(evt.Name()); - } - - auto event_semantics = get_type_semantics(evt.EventType()); - auto event_source_type = w.write_temp("%", - bind(event_semantics, typedef_name_type::EventSource, false)); - auto vtable_index = get_vmethod_index(add.Parent(), add) + 6; - bool is_generic_event = std::holds_alternative(event_semantics); - - // ICommand.CanExecuteChanged has a type mismatch: the .NET event type is EventHandler (non-generic), - // but the WinRT type is EventHandler. Use the non-generic EventHandlerEventSource which has - // Subscribe(EventHandler), matching the .NET event type after the fixup in write_event. - if (evt.Name() == "CanExecuteChanged" && is_generic_event) - { - auto parent_type_name = w.write_temp("%", bind(evt.Parent(), typedef_name_type::NonProjected, true)); - if (parent_type_name == "Microsoft.UI.Xaml.Input.ICommand" || parent_type_name == "Windows.UI.Xaml.Input.ICommand") - { - event_source_type = "global::WindowsRuntime.InteropServices.EventHandlerEventSource"; - is_generic_event = false; - } - } - - w.write(R"( -private % _eventSource_% -{ - get - {% - [MethodImpl(MethodImplOptions.NoInlining)] - % MakeEventSource() - { - _ = global::System.Threading.Interlocked.CompareExchange( - location1: ref field, - value: %, - comparand: null); - - return field; - } - - return field ?? MakeEventSource(); - } -} -)", - event_source_type, - event_source_field_name, - [&](writer& w) { - if (is_generic_event) { - w.write(R"( - [UnsafeAccessor(UnsafeAccessorKind.Constructor)] - [return: UnsafeAccessorType("%, WinRT.Interop")] - static extern object ctor(WindowsRuntimeObjectReference nativeObjectReference, int index); -)", - bind(event_semantics, typedef_name_type::EventSource)); - } - }, - event_source_type, - [&](writer& w) { - if (is_generic_event) { - w.write("Unsafe.As<%>(ctor(%, %))", - event_source_type, objref_name, vtable_index); - } else { - w.write("new %(%, %)", - event_source_type, objref_name, vtable_index); - } - }); - } - }); - } - } - - void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) - { - // Write the factory objref if there are constructors on this type. - if (size(composable_type.MethodList()) != 0) - { - write_static_objref_definition(w, composable_type, class_type); - } - - auto cache_object = bind(composable_type); - auto gc_pressure_amount = get_gc_pressure_amount(class_type); - auto gc_pressure = w.write_temp("%", - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); - }); - - auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); - auto marshaling_type = get_marshaling_type_name(class_type); - - for (auto&& method : composable_type.MethodList()) - { - method_signature signature{ method }; - auto params_without_objects = signature.params(); - params_without_objects.pop_back(); - params_without_objects.pop_back(); - - auto platform_attribute = write_platform_attribute_temp(w, composable_type); - - w.write(R"( -%% %%(%) - :base(%) -{ -if (GetType() == typeof(%)) -{ -% = NativeObjectReference; -} -%} -)", - platform_attribute, - visibility, - params_without_objects.empty() ? "" : "unsafe ", - // construct name - class_type.TypeName(), - bind_list(", ", params_without_objects), - // base - bind([&](writer& w) - { - if (params_without_objects.empty()) - { - w.write("default(WindowsRuntimeActivationTypes.DerivedComposed), %, %, %", - cache_object, - bind(default_type_semantics), - marshaling_type); - } - else - { - w.write("%.Instance, %, %, WindowsRuntimeActivationArgsReference.CreateUnsafe(new %(%))", - bind(method), - bind(default_type_semantics), - marshaling_type, - bind(method), - bind_list(", ", params_without_objects)); - } - }), - class_type.TypeName(), - bind(default_type_semantics), - gc_pressure); - - // Write activation callback method if the constructor has parameters. - // Otherwise use the base class to handle activation. - if (!params_without_objects.empty()) - { - write_static_composing_factory_method(w, composable_type, method); - } - } - - if (settings.reference_projection) - { - return; - } - - w.write(R"( -protected %(WindowsRuntimeActivationTypes.DerivedComposed _, WindowsRuntimeObjectReference activationFactoryObjectReference, in Guid iid, CreateObjectReferenceMarshalingType marshalingType) - :base(_, activationFactoryObjectReference, in iid, marshalingType) -{ -%} - -protected %(WindowsRuntimeActivationTypes.DerivedSealed _, WindowsRuntimeObjectReference activationFactoryObjectReference, in Guid iid, CreateObjectReferenceMarshalingType marshalingType) - :base(_, activationFactoryObjectReference, in iid, marshalingType) -{ -%} - -protected %(WindowsRuntimeActivationFactoryCallback.DerivedComposed activationFactoryCallback, in Guid iid, CreateObjectReferenceMarshalingType marshalingType, WindowsRuntimeActivationArgsReference additionalParameters) - :base(activationFactoryCallback, in iid, marshalingType, additionalParameters) -{ -%} - -protected %(WindowsRuntimeActivationFactoryCallback.DerivedSealed activationFactoryCallback, in Guid iid, CreateObjectReferenceMarshalingType marshalingType, WindowsRuntimeActivationArgsReference additionalParameters) - :base(activationFactoryCallback, in iid, marshalingType, additionalParameters) -{ -%} -)", - // WindowsRuntimeActivationTypes.DerivedComposed - class_type.TypeName(), - gc_pressure, - // WindowsRuntimeActivationTypes.DerivedSealed - class_type.TypeName(), - gc_pressure, - // WindowsRuntimeActivationFactoryCallback.DerivedComposed - class_type.TypeName(), - gc_pressure, - // WindowsRuntimeActivationFactoryCallback.DerivedSealed - class_type.TypeName(), - gc_pressure); - } - - void write_static_factory_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) - { - if (method.SpecialName()) - { - return; - } - method_signature signature{ method }; - auto return_type = w.write_temp("%", [&](writer& w) { - write_projection_return_type(w, signature); - }); - write_method(w, signature, method.Name(), return_type, method_target, "public "sv, ""sv, platform_attribute, std::nullopt); - } - - void write_static_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) - { - if (method.SpecialName()) - { - return; - } - method_signature signature{ method }; - auto return_type = w.write_temp("%", [&](writer& w) { - write_projection_return_type(w, signature); - }); - write_method(w, signature, method.Name(), return_type, method_target, "public "sv, "static "sv, platform_attribute, std::optional(std::pair(method.Parent(), method))); - } - - void write_static_factory_property(writer& w, Property const& prop, std::string_view prop_target, std::string_view platform_attribute = ""sv) - { - auto [getter, setter] = get_property_methods(prop); - auto getter_target = getter ? prop_target : ""; - auto setter_target = setter ? prop_target : ""; - write_property(w, prop.Name(), prop.Name(), write_prop_type(w, prop), - getter_target, setter_target, "public "sv, ""sv, platform_attribute, platform_attribute, - std::nullopt, - std::nullopt); - } - - void write_static_factory_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) - { - write_event(w, event.Name(), event, event_target, "public "sv, ""sv, platform_attribute, std::nullopt); - } - - void write_static_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) - { - write_event(w, event.Name(), event, event_target, "public "sv, "static "sv, platform_attribute, std::optional(std::tuple(event.Parent(), event, true))); - } - - void write_static_members(writer& w, TypeDef const& class_type) - { - std::map>, std::optional>>> properties; - - for (auto&& [interface_name, factory] : get_attributed_types(w, class_type)) - { - if (factory.statics) - { - write_static_objref_definition(w, factory.type, class_type); - auto cache_object = w.write_temp("%", bind(factory.type)); - - auto platform_attribute = write_platform_attribute_temp(w, factory.type); - w.write_each(factory.type.MethodList(), cache_object, platform_attribute); - w.write_each(factory.type.EventList(), cache_object, platform_attribute); - - // Merge property getters/setters, since such may be defined across interfaces - for (auto&& prop : factory.type.PropertyList()) - { - auto [getter, setter] = get_property_methods(prop); - auto prop_type = write_prop_type(w, prop); - - auto [prop_targets, inserted] = properties.try_emplace(std::string(prop.Name()), - prop_type, - getter ? cache_object : "", - getter ? platform_attribute : "", - setter ? cache_object : "", - setter ? platform_attribute : "", - !getter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)), - !setter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)) - ); - if (!inserted) - { - auto& [property_type, getter_target, getter_platform, setter_target, setter_platform, getter_prop, setter_prop] = prop_targets->second; - XLANG_ASSERT(property_type == prop_type); - if (getter) - { - XLANG_ASSERT(getter_target.empty()); - getter_target = cache_object; - getter_platform = platform_attribute; - getter_prop = std::optional(std::pair(prop.Parent(), prop)); - } - if (setter) - { - XLANG_ASSERT(setter_target.empty()); - setter_target = cache_object; - setter_platform = platform_attribute; - setter_prop = std::optional(std::pair(prop.Parent(), prop)); - } - XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); - } - } - } - } - - // Write properties with merged accessors - for (auto& [prop_name, prop_data] : properties) - { - auto& [prop_type, getter_target, getter_platform, setter_target, setter_platform, getter_prop, setter_prop] = prop_data; - write_property(w, prop_name, prop_name, prop_type, - getter_target, setter_target, "public "sv, "static "sv, getter_platform, setter_platform, - getter_prop, - setter_prop); - } - } - - void write_attributed_types(writer& w, TypeDef const& type) - { - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if (factory.activatable) - { - write_factory_constructors(w, factory.type, type); - } - else if (factory.composable) - { - write_composable_constructors(w, factory.type, type, factory.visible ? "public"sv : "protected"sv); - } - } - - write_static_members(w, type); - } - - void write_nongeneric_enumerable_members_using_static_abi_methods(writer& w, std::string const& objref_name) - { - w.write(R"( -IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => global::ABI.System.Collections.IEnumerableMethods.GetEnumerator(%); -)", -objref_name); - } - - void write_nongeneric_enumerator_members_using_static_abi_methods(writer& w, std::string const& objref_name) - { - w.write(R"( -public bool MoveNext() => global::ABI.System.Collections.IEnumeratorMethods.MoveNext(%); -public void Reset() => throw new NotSupportedException(); -public object Current => global::ABI.System.Collections.IEnumeratorMethods.Current(%); -)", -objref_name, objref_name); - } - - void write_nongeneric_enumerable_members(writer& w, std::string_view target) - { - w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(); -)", - target); - } - - void write_enumerable_members_using_static_abi_methods(writer& w, bool include_nongeneric, bool emit_explicit, std::string const& objref_name) - { - auto element = w.write_temp("%", bind(0)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerable<%>.", element) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto interop_method_name_prefix = w.write_temp("IEnumerableMethods_%_", escape_type_name_for_identifier(element, true)); - auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetEnumerator")] -static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IEnumerable'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); -)", - element, - interop_method_name_prefix, - element_interop_type_name -); - - w.write(R"( -%IEnumerator<%> %GetEnumerator() => %GetEnumerator(null, %); -)", -visibility, element, self, interop_method_name_prefix, objref_name); - - if (!include_nongeneric) return; - - if (emit_explicit) - { - w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => %GetEnumerator(null, %); -)", interop_method_name_prefix, objref_name); - } - else - { - w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -)"); - } - } - - void write_enumerable_members_using_idic(writer& w, bool include_nongeneric) - { - auto element = w.write_temp("%", bind(0)); - w.write(R"( -IEnumerator<%> global::System.Collections.Generic.IEnumerable<%>.GetEnumerator() => ((global::System.Collections.Generic.IEnumerable<%>)(WindowsRuntimeObject)this).GetEnumerator(); -)", - element, element, element); - - if (!include_nongeneric) return; - - w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => ((global::System.Collections.Generic.IEnumerable<%>)(WindowsRuntimeObject)this).GetEnumerator(); -)", element); - } - - void write_enumerator_members_using_idic(writer& w) - { - auto element = w.write_temp("%", bind(0)); - - w.write(R"( -bool global::System.Collections.Generic.IEnumerator<%>.MoveNext() => ((global::System.Collections.Generic.IEnumerator<%>)(WindowsRuntimeObject)this).MoveNext(); -void global::System.Collections.Generic.IEnumerator<%>.Reset() => ((global::System.Collections.Generic.IEnumerator<%>)(WindowsRuntimeObject)this).Reset(); -void global::System.Collections.Generic.IEnumerator<%>.Dispose() => ((global::System.Collections.Generic.IEnumerator<%>)(WindowsRuntimeObject)this).Dispose(); -% global::System.Collections.Generic.IEnumerator<%>.Current => ((global::System.Collections.Generic.IEnumerator<%>)(WindowsRuntimeObject)this).Current; -object IEnumerator.Current => Current; -)", - element, element, - element, element, - element, element, - element, element, element); - } - - void write_enumerator_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) - { - auto element = w.write_temp("%", bind(0)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerator<%>.", element) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto interop_method_name_prefix = w.write_temp("IEnumeratorMethods_%_", escape_type_name_for_identifier(element, true)); - auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "MoveNext")] -static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IEnumerator'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Current")] -static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IEnumerator'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); -)", -// MoveNext -interop_method_name_prefix, -element_interop_type_name, -// Current -element, -interop_method_name_prefix, -element_interop_type_name -); - - w.write(R"( -%bool %MoveNext() => %MoveNext(null, %); -%void %Reset() => throw new NotSupportedException(); -%void %Dispose() {} -%% %Current => %Current(null, %); -object IEnumerator.Current => Current; -)", -visibility, self, interop_method_name_prefix, objref_name, -visibility, self, -visibility, self, -visibility, element, self, interop_method_name_prefix, objref_name); - } - - void write_readonlydictionary_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyDictionary<%, %>.", key, value) : ""; - auto ireadonlycollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyCollection>.", key, value) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; - auto interop_method_name_prefix = w.write_temp("IReadOnlyDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); - auto key_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - auto value_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(1).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); -)", -key, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Keys -value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Values -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Count -value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Item -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // ContainsKey -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value // TryGetValue -); - - w.write(R"( -%IEnumerable<%> %Keys => %Keys(null, %); -%IEnumerable<%> %Values => %Values(null, %); -%int %Count => %Count(null, %); -%% %this[% key] => %Item(null, %, key); -%bool %ContainsKey(% key) => %ContainsKey(null, %, key); -%bool %TryGetValue(% key, out % value) => %TryGetValue(null, %, key, out value); -)", -visibility, key, self, interop_method_name_prefix, objref_name, -visibility, value, self, interop_method_name_prefix, objref_name, -visibility, ireadonlycollection, interop_method_name_prefix, objref_name, -visibility, value, self, key, interop_method_name_prefix, objref_name, -visibility, self, key, interop_method_name_prefix, objref_name, -visibility, self, key, value, interop_method_name_prefix, objref_name); - } - - void write_readonlydictionary_members_using_idic(writer& w, bool include_enumerable) - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - auto target = w.write_temp("((global::System.Collections.Generic.IReadOnlyDictionary<%, %>)(WindowsRuntimeObject)this)", key, value); - auto self = w.write_temp("global::System.Collections.Generic.IReadOnlyDictionary<%, %>.", key, value); - auto ireadonlycollection = w.write_temp("global::System.Collections.Generic.IReadOnlyCollection>.", key, value ); - w.write(R"( -IEnumerable<%> %Keys => %.Keys; -IEnumerable<%> %Values => %.Values; -int %Count => %.Count; -% %this[% key] => %[key]; -bool %ContainsKey(% key) => %.ContainsKey(key); -bool %TryGetValue(% key, out % value) => %.TryGetValue(key, out value); -)", - key, self, target, - value, self, target, - ireadonlycollection, target, - value, self, key, target, - self, key, target, - self, key, value, target); - - if (!include_enumerable) return; - auto enumerable_type = w.write_temp("IEnumerable>.", key, value); - w.write(R"( -IEnumerator> %GetEnumerator() => %.GetEnumerator(); -IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -)", - key, value, enumerable_type, target); - } - - void write_dictionary_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IDictionary<%, %>.", key, value) : ""; - auto icollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.ICollection>.", key, value) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; - auto interop_method_name_prefix = w.write_temp("IDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); - auto key_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - auto value_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(1).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key, % value); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key, % value); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IDictionary'2<%|%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); -)", -key, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Keys -value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Values -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Count -value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Item get -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Item set -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Add -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // ContainsKey -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Remove -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // TryGetValue -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Add -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Clear -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Contains -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // CopyTo -interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value // Remove -); - - w.write(R"( -%ICollection<%> %Keys => %Keys(null, %); -%ICollection<%> %Values => %Values(null, %); -%int %Count => %Count(null, %); -%bool %IsReadOnly => false; -%% %this[% key] -{ -get => %Item(null, %, key); -set => %Item(null, %, key, value); -} -%void %Add(% key, % value) => %Add(null, %, key, value); -%bool %ContainsKey(% key) => %ContainsKey(null, %, key); -%bool %Remove(% key) => %Remove(null, %, key); -%bool %TryGetValue(% key, out % value) => %TryGetValue(null, %, key, out value); -%void %Add(KeyValuePair<%, %> item) => %Add(null, %, item); -%void %Clear() => %Clear(null, %); -%bool %Contains(KeyValuePair<%, %> item) => %Contains(null, %, item); -%void %CopyTo(KeyValuePair<%, %>[] array, int arrayIndex) => %CopyTo(null, %, %, array, arrayIndex); -bool ICollection>.Remove(KeyValuePair<%, %> item) => %Remove(null, %, item); -)", -visibility, key, self, interop_method_name_prefix, objref_name, //Keys -visibility, value, self, interop_method_name_prefix, objref_name, // Values -visibility, icollection, interop_method_name_prefix, objref_name, // Count -visibility, icollection, // IsReadOnly -visibility, value, self, key, interop_method_name_prefix, objref_name, interop_method_name_prefix, objref_name, // Indexer -visibility, self, key, value, interop_method_name_prefix, objref_name, -visibility, self, key, interop_method_name_prefix, objref_name, -visibility, self, key, interop_method_name_prefix, objref_name, -visibility, self, key, value, interop_method_name_prefix, objref_name, -visibility, icollection, key, value, interop_method_name_prefix, objref_name, -visibility, icollection, interop_method_name_prefix, objref_name, -visibility, icollection, key, value, interop_method_name_prefix, objref_name, -visibility, icollection, key, value, interop_method_name_prefix, objref_name, enumerableObjRefName, -key, value, key, value, interop_method_name_prefix, objref_name); - } - - void write_dictionary_members_using_idic(writer& w, bool include_enumerable) - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - auto target = w.write_temp("((global::System.Collections.Generic.IDictionary<%, %>)(WindowsRuntimeObject)this)", key, value); - auto self = w.write_temp("global::System.Collections.Generic.IDictionary<%, %>.", key, value); - auto icollection = w.write_temp("global::System.Collections.Generic.ICollection>.", key, value ); - w.write(R"( -ICollection<%> %Keys => %.Keys; -ICollection<%> %Values => %.Values; -int %Count => %.Count; -bool %IsReadOnly => %.IsReadOnly; -% %this[% key] -{ -get => %[key]; -set => %[key] = value; -} -void %Add(% key, % value) => %.Add(key, value); -bool %ContainsKey(% key) => %.ContainsKey(key); -bool %Remove(% key) => %.Remove(key); -bool %TryGetValue(% key, out % value) => %.TryGetValue(key, out value); -void %Add(KeyValuePair<%, %> item) => %.Add(item); -void %Clear() => %.Clear(); -bool %Contains(KeyValuePair<%, %> item) => %.Contains(item); -void %CopyTo(KeyValuePair<%, %>[] array, int arrayIndex) => %.CopyTo(array, arrayIndex); -bool ICollection>.Remove(KeyValuePair<%, %> item) => %.Remove(item); -)", - key, self, target, - value, self, target, - icollection, target, - icollection, target, - value, self, key, target, target, - self, key, value, target, - self, key, target, - self, key, target, - self, key, value, target, - icollection, key, value, target, - icollection, target, - icollection, key, value, target, - icollection, key, value, target, - key, value, key, value, target); - - if (!include_enumerable) return; - auto enumerable_type = w.write_temp("IEnumerable>.", key, value); - w.write(R"( -IEnumerator> %GetEnumerator() => %.GetEnumerator(); -IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -)", - key, value, enumerable_type, target); - } - - void write_readonlylist_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) - { - auto element = w.write_temp("%", bind(0)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyList<%>.", element) : ""; - auto ireadonlycollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyCollection<%>.", element) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto objRefName = w.write_temp("%", objref_name); - auto interop_method_name_prefix = w.write_temp("IReadOnlyListMethods_%_", escape_type_name_for_identifier(element, true)); - auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IReadOnlyList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, int index); -)", -interop_method_name_prefix, element_interop_type_name, // Count -element, interop_method_name_prefix, element_interop_type_name // Item -); - - w.write(R"( -%int %Count => %Count(null, %); -% -%% %this[int index] => %Item(null, %, index); -)", -visibility, ireadonlycollection, interop_method_name_prefix, objRefName, -!emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("ReadOnlyListItem")])" : "", -visibility, element, self, interop_method_name_prefix, objRefName); - - } - - void write_readonlylist_members_using_idic(writer& w, bool include_enumerable) - { - auto element = w.write_temp("%", bind(0)); - auto target = w.write_temp("((global::System.Collections.Generic.IReadOnlyList<%>)(WindowsRuntimeObject)this)", element); - auto self = w.write_temp("global::System.Collections.Generic.IReadOnlyList<%>.", element); - auto ireadonlycollection = w.write_temp("global::System.Collections.Generic.IReadOnlyCollection<%>.", element); - w.write(R"( -int %Count => %.Count; -% %this[int index] => %[index]; -)", - ireadonlycollection, target, - element, self, target); - - if (!include_enumerable) return; - auto enumerable_type = w.write_temp("IEnumerable<%>.", element); - w.write(R"( -IEnumerator<%> %GetEnumerator() => %.GetEnumerator(); -IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -)", - element, enumerable_type, target); - } - - void write_nongeneric_list_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) - { - auto self = emit_explicit ? "global::System.Collections.IList." : ""; - auto icollection = emit_explicit ? "global::System.Collections.ICollection." : ""; - auto visibility = emit_explicit ? "" : "public "; - auto target = "global::ABI.System.Collections.IListMethods"; - - w.write(R"( -%int %Count => %.Count(%); -%bool %IsSynchronized => false; -%object %SyncRoot => this; -%void %CopyTo(Array array, int index) => %.CopyTo(%, array, index); -% -%object %this[int index] -{ -get => %.Item(%, index); -set => %.Item(%, index, value); -} -%bool %IsFixedSize => false; -%bool %IsReadOnly => false; -%int %Add(object value) => %.Add(%, value); -%void %Clear() => %.Clear(%); -%bool %Contains(object value) => %.Contains(%, value); -%int %IndexOf(object value) => %.IndexOf(%, value); -%void %Insert(int index, object value) => %.Insert(%, index, value); -%void %Remove(object value) => %.Remove(%, value); -%void %RemoveAt(int index) => %.RemoveAt(%, index); -)", -visibility, icollection, target, objref_name, -visibility, icollection, -visibility, icollection, -visibility, icollection, target, objref_name, -!emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("NonGenericListItem")])" : "", -visibility, self, -target, objref_name, -target, objref_name, -visibility, self, -visibility, self, -visibility, self, target, objref_name, -visibility, self, target, objref_name, -visibility, self, target, objref_name, -visibility, self, target, objref_name, -visibility, self, target, objref_name, -visibility, self, target, objref_name, -visibility, self, target, objref_name); - } - - void write_nongeneric_list_members_using_idic(writer& w, std::string_view target, bool include_enumerable, bool emit_explicit) - { - auto self = emit_explicit ? "global::System.Collections.IList." : ""; - auto icollection = emit_explicit ? "global::System.Collections.ICollection." : ""; - auto visibility = emit_explicit ? "" : "public "; - w.write(R"( -%int %Count => %.Count; -%bool %IsSynchronized => %.IsSynchronized; -%object %SyncRoot => %.SyncRoot; -%void %CopyTo(Array array, int index) => %.CopyTo(array, index); -% -%object %this[int index] -{ -get => %[index]; -set => %[index] = value; -} -%bool %IsFixedSize => %.IsFixedSize; -%bool %IsReadOnly => %.IsReadOnly; -%int %Add(object value) => %.Add(value); -%void %Clear() => %.Clear(); -%bool %Contains(object value) => %.Contains(value); -%int %IndexOf(object value) => %.IndexOf(value); -%void %Insert(int index, object value) => %.Insert(index, value); -%void %Remove(object value) => %.Remove(value); -%void %RemoveAt(int index) => %.RemoveAt(index); -)", - visibility, icollection, target, - visibility, icollection, target, - visibility, icollection, target, - visibility, icollection, target, - !emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("NonGenericListItem")])" : "", - visibility, self, - target, - target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target, - visibility, self, target); - - if (!include_enumerable) return; - w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(); -)", - target); - } - - void write_list_members_using_idic(writer& w, bool include_enumerable) - { - auto element = w.write_temp("%", bind(0)); - auto target = w.write_temp("((global::System.Collections.Generic.IList<%>)(WindowsRuntimeObject)this)", element); - auto self = w.write_temp("global::System.Collections.Generic.IList<%>.", element); - auto icollection = w.write_temp("global::System.Collections.Generic.ICollection<%>.", element); - - w.write(R"( -int %Count => %.Count; -bool %IsReadOnly => %.IsReadOnly; -% %this[int index] -{ -get => %[index]; -set => %[index] = value; -} -int %IndexOf(% item) => %.IndexOf(item); -void %Insert(int index, % item) => %.Insert(index, item); -void %RemoveAt(int index) => %.RemoveAt(index); -void %Add(% item) => %.Add(item); -void %Clear() => %.Clear(); -bool %Contains(% item) => %.Contains(item); -void %CopyTo(%[] array, int arrayIndex) => %.CopyTo(array, arrayIndex); -bool %Remove(% item) => %.Remove(item); -)", -icollection, target, -icollection, target, -element, self, target, target, -self, element, target, -self, element, target, -self, target, -icollection, element, target, -icollection, target, -icollection, element, target, -icollection, element, target, -icollection, element, target); - - if (!include_enumerable) return; - auto enumerable_type = w.write_temp("IEnumerable<%>.", element); - w.write(R"( -IEnumerator<%> %GetEnumerator() => %.GetEnumerator(); -IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -)", -element, enumerable_type, target); - } - - void write_list_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) - { - auto element = w.write_temp("%", bind(0)); - auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IList<%>.", element) : ""; - auto icollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.ICollection<%>.", element) : ""; - auto visibility = emit_explicit ? "" : "public "; - auto objRefName = w.write_temp("%", objref_name); - auto interop_method_name_prefix = w.write_temp("IListMethods_%_", escape_type_name_for_identifier(element, true)); - auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, int index); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, int index, % value); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "IndexOf")] -static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Insert")] -static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, int index, % item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RemoveAt")] -static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, int index); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % item); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); - -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#corlib>IList'1<%>Methods, WinRT.Interop")] object _, WindowsRuntimeObjectReference objRef, % item); -)", -interop_method_name_prefix, element_interop_type_name, // Count -element, interop_method_name_prefix, element_interop_type_name, // Item get -interop_method_name_prefix, element_interop_type_name, element, // Item set -interop_method_name_prefix, element_interop_type_name, element, // IndexOf -interop_method_name_prefix, element_interop_type_name, element, // Insert -interop_method_name_prefix, element_interop_type_name, // RemoveAt -interop_method_name_prefix, element_interop_type_name, element, // Add -interop_method_name_prefix, element_interop_type_name, // Clear -interop_method_name_prefix, element_interop_type_name, element, // Contains -interop_method_name_prefix, element_interop_type_name, element, // CopyTo -interop_method_name_prefix, element_interop_type_name, element // Remove -); - - w.write(R"( -%int %Count => %Count(null, %); -%bool %IsReadOnly => false; -% -%% %this[int index] -{ -get => %Item(null, %, index); -set => %Item(null, %, index, value); -} -%int %IndexOf(% item) => %IndexOf(null, %, item); -%void %Insert(int index, % item) => %Insert(null, %, index, item); -%void %RemoveAt(int index) => %RemoveAt(null, %, index); -%void %Add(% item) => %Add(null, %, item); -%void %Clear() => %Clear(null, %); -%bool %Contains(% item) => %Contains(null, %, item); -%void %CopyTo(%[] array, int arrayIndex) => %CopyTo(null, %, array, arrayIndex); -%bool %Remove(% item) => %Remove(null, %, item); -)", - visibility, icollection, interop_method_name_prefix, objref_name, //Count - visibility, icollection, //IsReadOnly - !emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("ListItem")])" : "", //Indexer - visibility, element, self, interop_method_name_prefix, objref_name, interop_method_name_prefix, objref_name, //Indexer - visibility, self, element, interop_method_name_prefix, objref_name, //IndexOf - visibility, self, element, interop_method_name_prefix, objref_name, //Insert - visibility, self, interop_method_name_prefix, objref_name, //RemoveAt - visibility, icollection, element, interop_method_name_prefix, objref_name, //Add - visibility, icollection, interop_method_name_prefix, objref_name, //Clear - visibility, icollection, element, interop_method_name_prefix, objref_name, //Contains - visibility, icollection, element, interop_method_name_prefix, objref_name, //CopyTo - visibility, icollection, element, interop_method_name_prefix, objref_name); //Remove - } - - void write_idisposable_members(writer& w) - { - w.write(R"( -void global::System.IDisposable.Dispose() => ((global::System.IDisposable)(WindowsRuntimeObject)this).Dispose(); -)"); - } - - void write_idisposable_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) - { - auto self = emit_explicit ? "global::System.IDisposable." : ""; - auto visibility = emit_explicit ? "" : "public "; - w.write(R"( -%void %Dispose() => global::ABI.System.IDisposableMethods.Dispose(%); -)", -visibility, self, objref_name); - } - - void write_notify_data_error_info_members_using_idic(writer& w) - { - auto target = "((global::System.ComponentModel.INotifyDataErrorInfo)(WindowsRuntimeObject)this)"; - auto self = "global::System.ComponentModel.INotifyDataErrorInfo."; - - w.write(R"( -global::System.Collections.IEnumerable %GetErrors(string propertyName) => %.GetErrors(propertyName); - -event global::System.EventHandler %ErrorsChanged -{ -add => %.ErrorsChanged += value; -remove => %.ErrorsChanged -= value; -} -bool %HasErrors {get => %.HasErrors; } -)", - self, target, - self, target, target, - self, target); - } - - void write_notify_data_error_info_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) - { - auto self = emit_explicit ? "global::System.ComponentModel.INotifyDataErrorInfo." : ""; - auto visibility = emit_explicit ? "" : "public "; - - w.write(R"( -%global::System.Collections.IEnumerable %GetErrors(string propertyName) => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.GetErrors(%, propertyName); - -%event global::System.EventHandler %ErrorsChanged -{ -add => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.ErrorsChanged(this, %).Subscribe(value); -remove => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.ErrorsChanged(this, %).Unsubscribe(value); -} -%bool %HasErrors {get => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.HasErrors(%); } -)", -visibility, self, objref_name, -visibility, self, objref_name, objref_name, -visibility, self, objref_name); - } - - void write_custom_mapped_type_members(writer& w, mapped_type const& mapping, bool is_private, std::string objref_name) - { - if (mapping.abi_name == "IIterable`1") - { - write_enumerable_members_using_static_abi_methods(w, true, is_private, objref_name); - } - else if (mapping.abi_name == "IIterator`1") - { - write_enumerator_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.abi_name == "IMapView`2") - { - write_readonlydictionary_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.abi_name == "IMap`2") - { - write_dictionary_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.abi_name == "IVectorView`1") - { - write_readonlylist_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.abi_name == "IVector`1") - { - write_list_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.mapped_namespace == "System.Collections" && mapping.mapped_name == "IEnumerable") - { - write_nongeneric_enumerable_members_using_static_abi_methods(w, objref_name); - } - else if (mapping.mapped_namespace == "System.Collections" && mapping.mapped_name == "IEnumerator") - { - write_nongeneric_enumerator_members_using_static_abi_methods(w, objref_name); - } - else if (mapping.mapped_namespace == "System.Collections" && mapping.mapped_name == "IList") - { - write_nongeneric_list_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.mapped_namespace == "System" && mapping.mapped_name == "IDisposable") - { - write_idisposable_members_using_static_abi_methods(w, is_private, objref_name); - } - else if (mapping.mapped_namespace == "System.ComponentModel" && mapping.mapped_name == "INotifyDataErrorInfo") - { - write_notify_data_error_info_members_using_static_abi_methods(w, is_private, objref_name); - } - } - - std::pair find_property_interface(writer& w, TypeDef const& setter_iface, std::string_view prop_name) - { - TypeDef getter_iface; - - auto search_interface = [&](TypeDef const& type) - { - for (auto&& prop : type.PropertyList()) - { - if (prop.Name() == prop_name) - { - getter_iface = type; - return true; - } - } - return false; - }; - - std::function search_interfaces = [&](TypeDef const& type) - { - for (auto&& iface : type.InterfaceImpl()) - { - auto semantics = get_type_semantics(iface.Interface()); - if (for_typedef(w, semantics, [&](auto&& type) - { - return (setter_iface != type) && (search_interface(type) || search_interfaces(type)); - })) { - return true; - } - } - return false; - }; - - std::function search_interfaces_from_attributes = [&](TypeDef const& type) - { - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if (factory.statics && factory.type && (search_interface(factory.type) || search_interfaces(factory.type))) - { - return true; - } - } - - return false; - }; - - - // first search base interfaces for property getter - if (search_interfaces(setter_iface)) - { - return { getter_iface, true }; - } - - // then search peer exclusive-to interfaces and their bases - if (auto exclusive_to_attr = get_attribute(setter_iface, "Windows.Foundation.Metadata", "ExclusiveToAttribute")) - { - auto sig = exclusive_to_attr.Value(); - auto const& fixed_args = sig.FixedArgs(); - XLANG_ASSERT(fixed_args.size() == 1); - auto sys_type = std::get(std::get(fixed_args[0].value).value); - auto exclusive_to_type = setter_iface.get_cache().find_required(sys_type.name); - if (search_interfaces(exclusive_to_type)) - { - return { getter_iface, false }; - } - - if (search_interfaces_from_attributes(exclusive_to_type)) - { - return { getter_iface, false }; - } - } - - throw_invalid("Could not find property getter interface"); - } - - void write_class_members(writer& w, TypeDef const& type, bool is_interface_impl_type) - { - std::set writtenInterfaces; - std::map>, std::optional>>> properties; - auto fast_abi_class_val = get_fast_abi_class_for_class(type); - - auto write_class_interface = [&](TypeDef const& interface_type, bool is_default_interface, bool is_overridable_interface, bool is_protected_interface, type_semantics semantics) - { - // When writing derived interfaces of interfaces, we can sometimes encounter duplicate interfaces. - // To prevent writing them multiple times, we catch them here. - if (writtenInterfaces.find(interface_type) != writtenInterfaces.end()) - { - return; - } - writtenInterfaces.insert(interface_type); - - auto interface_name = write_type_name_temp(w, interface_type); - auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); - - auto static_iface_target = w.write_temp("%", bind(semantics, typedef_name_type::StaticAbiClass, true)); - auto target = is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())"); - - auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type); - auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; - - // Write the 'IWindowsRuntimeInterface' implementation for non-default interfaces and for the - // default interface if it isn't exclusive. Also skip this when generating reference projections, - // since in this case we omit declaring the 'IWindowsRuntimeInterface' types entirely. Otherwise, - // write the default interface in composable scenarios using its own function. - if ((!is_exclusive_to(interface_type) || is_overridable_interface) && !settings.reference_projection) - { - w.write(R"( -WindowsRuntimeObjectReferenceValue IWindowsRuntimeInterface<%>.GetInterface() -{ -return %.AsValue(); -} -)", - interface_name, - bind(interface_type)); - } - else if (is_default_interface && !type.Flags().Sealed()) - { - bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); - - w.write(R"( -internal %WindowsRuntimeObjectReferenceValue GetDefaultInterface() -{ -return %.AsValue(); -} -)", - has_base_type ? "new " : "", - bind(interface_type)); - } - - if (auto mapping = get_mapped_type(interface_type.TypeNamespace(), interface_type.TypeName()); mapping && mapping->has_custom_members_output) - { - bool is_private = is_implemented_as_private_mapped_interface(w, type, interface_type); - auto objref_name = w.write_temp("%", bind(semantics)); - write_custom_mapped_type_members(w, *mapping, is_private, objref_name); - return; - } - - auto platform_attribute = write_platform_attribute_temp(w, interface_type); - - w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, platform_attribute, semantics_for_abi_call); - w.write_each(interface_type.EventList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, semantics_for_abi_call, !is_fast_abi_iface || is_default_interface); - - // Merge property getters/setters, since such may be defined across interfaces - for (auto&& prop : interface_type.PropertyList()) - { - MethodDef getter, setter; - std::tie(getter, setter) = get_property_methods(prop); - auto prop_type = write_prop_type(w, prop); - auto is_private = getter && is_implemented_as_private_method(w, type, getter); // for explicitly implemented interfaces, assume there is always a get. - auto property_name = is_private ? w.write_temp("%.%", interface_name, prop.Name()) : std::string(prop.Name()); - auto [prop_targets, inserted] = properties.try_emplace(property_name, - prop_type, - getter ? target : "", - getter ? platform_attribute : "", - setter ? target : "", - setter ? platform_attribute : "", - is_overridable_interface, - !is_protected_interface && !is_overridable_interface, // By default, an overridable member is protected. - is_private, - getter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt, - setter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt - ); - if (!inserted) - { - auto& [property_type, getter_target, getter_platform, setter_target, setter_platform, is_overridable, is_public, _, getter_prop, setter_prop] = prop_targets->second; - XLANG_ASSERT(property_type == prop_type); - if (getter) - { - XLANG_ASSERT(getter_target.empty()); - getter_target = target; - getter_platform = platform_attribute; - getter_prop = std::optional(std::pair(semantics_for_abi_call, prop)); - } - if (setter) - { - XLANG_ASSERT(setter_target.empty()); - setter_target = target; - setter_platform = platform_attribute; - setter_prop = std::optional(std::pair(semantics_for_abi_call, prop)); - } - is_overridable |= is_overridable_interface; - is_public |= !is_overridable_interface && !is_protected_interface; - XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); - } - // If this interface is overridable or private then we need to emit an explicit implementation of the property for that interface. - if (is_overridable_interface || is_private) - { - w.write("\n%% %.% {%%}", - platform_attribute, - prop_type, - bind(interface_type, typedef_name_type::CCW, false), - prop.Name(), - bind([&](writer& w) - { - bool base_getter{}; - std::string base_getter_platform_attribute{}; - TypeDef getter_property_iface; - if (!getter) - { - auto property_interface = find_property_interface(w, interface_type, prop.Name()); - base_getter = property_interface.second; - getter_property_iface = property_interface.first; - base_getter_platform_attribute = write_platform_attribute_temp(w, property_interface.first); - - } - if (getter || base_getter) - { - w.write("%get => %; ", base_getter_platform_attribute, bind([&](writer& w) { - if (is_private) - { - auto iface = base_getter ? getter_property_iface : prop.Parent(); - w.write("%", bind(iface, prop, - w.write_temp("%", bind(iface)))); - } - else - { - w.write("%", prop.Name()); - } - })); - } - }), - bind([&](writer& w) - { - if (setter) - { - w.write("set => %;", bind([&](writer& w) { - if (is_private) - { - w.write("%", bind(prop.Parent(), prop, - w.write_temp("%", bind(prop.Parent())))); - } - else - { - w.write("% = value", prop.Name()); - } - })); - } - })); - } - } - }; - - if (is_interface_impl_type) - { - write_class_interface(type, false, false, false, type); - } - - std::function write_class_interfaces = [&](TypeDef const& type) - { - for (auto&& ii : type.InterfaceImpl()) - { - auto is_default_interface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); - auto is_overridable_interface = has_attribute(ii, "Windows.Foundation.Metadata", "OverridableAttribute"); - auto is_protected_interface = has_attribute(ii, "Windows.Foundation.Metadata", "ProtectedAttribute"); - auto semantics = get_type_semantics(ii.Interface()); - - for_typedef(w, semantics, [&](auto&& type) - { - write_class_interface(type, is_default_interface, is_overridable_interface, is_protected_interface, is_interface_impl_type ? type : semantics); - if (is_interface_impl_type) - { - write_class_interfaces(type); - } - }); - } - }; - - for_typedef(w, type, [&](auto type) - { - write_class_interfaces(type); - }); - - // Write properties with merged accessors - for (auto& [prop_name, prop_data] : properties) - { - auto& [prop_type, getter_target, getter_platform, setter_target, setter_platform, is_overridable, is_public, is_private, getter_prop, setter_prop] = prop_data; - if (is_private) continue; - std::string_view access_spec = is_public ? "public "sv : "protected "sv; - std::string_view method_spec = is_overridable ? "virtual "sv : ""sv; - write_property(w, prop_name, prop_name, prop_type, - getter_prop.has_value() ? w.write_temp("%", bind(getter_prop.value().first)) : getter_target, - setter_prop.has_value() ? w.write_temp("%", bind(setter_prop.value().first)) : setter_target, - access_spec, method_spec, getter_platform, setter_platform, getter_prop, setter_prop); - } - } - - void write_convert_to_unmanaged_method_struct(writer& w, TypeDef const& type) - { - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - w.write("public static % ConvertToUnmanaged(% value)\n{\nreturn new() {\n%\n};\n}\n", - abi_name, - projection_name, - bind_list([](writer& w, auto&& field) - { - auto semantics = get_type_semantics(field.Signature().Type()); - auto field_name = field.Name(); - - call(semantics, - [&](object_type) - { - w.write(" % = WindowsRuntimeObjectMarshaller.ConvertToUnmanaged(value.%)", field_name, field_name); - }, - [&](guid_type) - { - w.write(" % = value.%", field_name, field_name); - }, - [&](type_type) - { - w.write(" % = value.%", field_name, field_name); - }, - [&](type_definition const& td) - { - auto field_abi_name = w.write_temp("%", bind(td, typedef_name_type::ABI, false)); - switch (get_category(td)) - { - case category::interface_type: - w.write(" // Unsupported interface_type for %", field_name); - break; - case category::class_type: - w.write(" % = %Marshaller.ConvertToUnmanaged(value.%)", field_name, field_abi_name, field_name); - break; - case category::delegate_type: - w.write(" % = WindowsRuntimeDelegateMarshaller.ConvertToUnmanaged(value.%)", field_name, field_name); - break; - case category::enum_type: - w.write(" % = value.%", field_name, field_name); - // TODO: consider arrays - break; - case category::struct_type: - if (!is_type_blittable(td)) - { - w.write(" % = %Marshaller.ConvertToUnmanaged(value.%)", field_name, field_abi_name, field_name); - } - else - { - w.write(" % = value.%", field_name, field_name); - } - break; - default: - w.write(" // Unsupported type_definition for %", field_name); - break; - } - }, - [&](generic_type_index) - { - w.write(" // TODO: generic_type_index for ", field_name); - }, - [&](generic_type_instance const& td) - { - call(td.generic_args[0], - [&](fundamental_type const& gtd) - { - w.write(" % = ABI.System.%Marshaller.BoxToUnmanaged(value.%).DetachThisPtrUnsafe()", field_name, to_string(gtd), field_name); - }, - [&](auto const&) { w.write(" // TODO: Handle generic_type_instance for other non fundamental_type types"); } - ); - - }, - [&](generic_type_param) - { - w.write(" // TODO: generic_type_param for %", field_name); - }, - [&](fundamental_type const& td) - { - if (td == fundamental_type::String) - { - // TODO: replace with cswinrt 3.0 - w.write(" % = HStringMarshaller.ConvertToUnmanaged(value.%)", field_name, field_name); - } - else - { - w.write(" % = value.%", field_name, field_name); - } - }); - }, ",\n", type.FieldList()) - ); - } - - void write_convert_to_managed_method_struct(writer& w, TypeDef const& type) - { - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - w.write("public static % ConvertToManaged(% value)\n{\nreturn new %%\n%\n%;\n}\n", - projection_name, - abi_name, - projection_name, - settings.component ? "(){" : "(", - bind_list([](writer& w, auto&& field) - { - auto semantics = get_type_semantics(field.Signature().Type()); - auto field_name = field.Name(); - auto field_prefix = settings.component ? w.write_temp(" % = ", field_name) : " "; - - call(semantics, - [&](object_type) - { - w.write("%WindowsRuntimeObjectMarshaller.ConvertToManaged(value.%)", field_prefix, field_name); - }, - [&](guid_type) - { - w.write("%value.%", field_prefix, field_name); - }, - [&](type_type) - { - w.write("%value.%", field_prefix, field_name); - }, - [&](type_definition const& td) - { - auto field_abi_name = w.write_temp("%", bind(td, typedef_name_type::ABI, false)); - switch (get_category(td)) - { - case category::interface_type: - w.write(" // Unsupported interface_type for %", field_name); - break; - case category::class_type: - w.write("%%Marshaller.ConvertToManaged(value.%)", field_prefix, field_abi_name, field_name); - break; - case category::delegate_type: - w.write("%WindowsRuntimeDelegateMarshaller.ConvertToManaged(value.%)", field_prefix, field_name); - break; - case category::enum_type: - w.write("%value.%", field_prefix, field_name); - // TODO: array case - break; - case category::struct_type: - if (!is_type_blittable(td)) - { - w.write("%%Marshaller.ConvertToManaged(value.%)", field_prefix, field_abi_name, field_name); - } - else - { - w.write("%value.%", field_prefix, field_name); - } - break; - default: - w.write(" // Unsupported type_definition for %", field_name); - break; - } - }, - [&](generic_type_index) - { - w.write(" // TODO: generic_type_index for %", field_name); - }, - [&](generic_type_instance const& td) - { - XLANG_ASSERT(td.generic_args.size() == 1); // Only applicable to structs - call(td.generic_args[0], - [&](fundamental_type const& gtd) - { - w.write("%ABI.System.%Marshaller.UnboxToManaged(value.%)", field_prefix, to_string(gtd), field_name); - }, - [&](auto const&) - { - w.write(" // TODO: Handle generic_type_instance for other non fundamental_type types"); - } - ); - }, - [&](generic_type_param) - { - w.write(" // TODO: generic_type_param for %", field_name); - }, - [&](fundamental_type const& td) - { - if (td == fundamental_type::String) - { - w.write("%HStringMarshaller.ConvertToManaged(value.%)", field_prefix, field_name); - } - else - { - w.write("%value.%", field_prefix, field_name); - } - }); - }, ",\n", type.FieldList()), - settings.component ? "}" : ")" - ); - } - - void write_dispose_method_struct(writer& w, TypeDef const& type) - { - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - w.write("public static void Dispose(% value)\n{\n%}\n", - abi_name, - bind_list([](writer& w, auto&& field) - { - auto semantics = get_type_semantics(field.Signature().Type()); - auto field_name = field.Name(); - call(semantics, - [&](object_type) - { - w.write("WindowsRuntimeUnknownMarshaller.Free(value.%);\n", field_name); - }, - [&](guid_type) - { - w.write("// TODO: guid_type %\n", field_name); - }, - [&](type_type) - { - w.write("// TODO: type_type %\n", field_name); - }, - [&](type_definition const& td) - { - switch (get_category(td)) - { - case category::interface_type: - case category::class_type: - case category::delegate_type: - w.write("WindowsRuntimeUnknownMarshaller.Free(value.%);\n", field_name); - break; - case category::struct_type: - if (!is_type_blittable(td)) - { - if (td.TypeNamespace() == "Windows.Foundation" && - (td.TypeName() == "DateTime" || - td.TypeName() == "TimeSpan" || - td.TypeName() == "HResult")) - { - // These types require marshaling due to being custom mapped, - // but are still blittable and don't have disposer. - break; - } - - w.write("%Marshaller.Dispose(value.%);\n", - bind(td, typedef_name_type::ABI, true), - field_name); - } - break; - default: - break; - } - }, - [&](generic_type_index) - { - w.write("// TODO: generic_type_index for %\n", field_name); - }, - [&](generic_type_instance td) - { - XLANG_ASSERT(td.generic_args.size() == 1); // Only applicable to structs - call(td.generic_args[0], - [&](fundamental_type) - { - w.write("WindowsRuntimeUnknownMarshaller.Free(value.%);\n", field_name); - }, - [&](auto const&) - { - w.write(" // TODO: Handle generic_type_instance for other non fundamental_type types"); - } - ); - }, - [&](generic_type_param) - { - w.write("// TODO: generic_type_param for %\n", field_name); - }, - [&](fundamental_type const& type) - { - if (type == fundamental_type::String) - { - w.write("HStringMarshaller.Free(value.%);\n", field_name); - } - }); - }, "", type.FieldList()) - ); - } - - void write_struct_and_enum_marshaller_class(writer& w, TypeDef const& type) - { - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - - w.write( -R"(public static unsafe class %Marshaller -{ -)", type.TypeName()); - - if (!is_type_blittable(type)) - { - write_convert_to_unmanaged_method_struct(w, type); - write_convert_to_managed_method_struct(w, type); - write_dispose_method_struct(w, type); - } - - w.write( -R"(public static WindowsRuntimeObjectReferenceValue BoxToUnmanaged(%? value) -{ - return WindowsRuntimeValueTypeMarshaller.BoxToUnmanaged(value, %, in %); -} -)", projection_name, use_tracker_object_support(type) ? "CreateComInterfaceFlags.TrackerSupport" : "CreateComInterfaceFlags.None", bind(type)); - - if (!is_type_blittable(type)) - { - w.write( -R"(public static %? UnboxToManaged(void* value) -{ - %? abi = WindowsRuntimeValueTypeMarshaller.UnboxToManaged<%>(value); - return abi.HasValue ? ConvertToManaged(abi.GetValueOrDefault()) : null; -} -} -)", projection_name, abi_name, abi_name); - } - else - { - w.write( - R"(public static %? UnboxToManaged(void* value) -{ - return WindowsRuntimeValueTypeMarshaller.UnboxToManaged<%>(value); -} -} - -)", projection_name, projection_name); - } - } - - void write_reference_impl(writer& w, TypeDef const& type) - { - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - auto result_param = is_type_blittable(type) ? projection_name : abi_name; - std::string guid_sig = w.write_temp("%", bind(type)); - std::string ireference_guid_sig = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};" + guid_sig + ")"; - - w.write( -R"(% static unsafe class %ReferenceImpl -{ - [FixedAddressValueType] - private static readonly ReferenceVftbl Vftbl; - - static %ReferenceImpl() - { - *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; - Vftbl.get_Value = &get_Value; - } - - public static nint Vtable - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)Unsafe.AsPointer(in Vftbl); - } - - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] - public static int get_Value(void* thisPtr, void* result) - { - if (result is null) - { - return unchecked((int)0x80004003); - } - - try - {)", - // For component types, the IReferenceImpl is referenced by cswinrtgen - // as part of generating the vtable for structs. - settings.component ? "public" : "file", - type.TypeName(), - type.TypeName()); - - if (is_type_blittable(type)) - { - w.write( -R"( - var value = (%)(ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr)); - *(%*)result = value;)" // Explicit cast back to the correct type - , result_param, result_param); - } - else - { - if (get_category(type) == category::delegate_type) - { - w.write( -R"( - % unboxedValue = (%)ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - % value = %Marshaller.ConvertToUnmanaged(unboxedValue).DetachThisPtrUnsafe(); - *(%*)result = value;)" - , projection_name, projection_name, result_param, type.TypeName(), result_param); - } - else - { - w.write( -R"( - % unboxedValue = (%)ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - % value = %Marshaller.ConvertToUnmanaged(unboxedValue); - *(%*)result = value;)" - , projection_name, projection_name, result_param, type.TypeName(), result_param); - } - } - - w.write(R"( - return 0; - } - catch (Exception e) - { - return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); - } - } - - public static ref readonly Guid IID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref %; - } -} - -)", bind(type)); - } - - void write_struct_and_enum_com_wrappers_marshaller_attribute_impl(writer& w, TypeDef const& type) - { - auto name = type.TypeName(); - auto projection_name = w.write_temp("%", bind(type)); - auto abi_name = w.write_temp("%", bind(type)); - auto iid_property_name = w.write_temp("%", bind(type)); - w.write( -R"(internal sealed unsafe class %ComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute -{ - public override void* GetOrCreateComInterfaceForObject(object value) - { - return WindowsRuntimeComWrappersMarshal.GetOrCreateComInterfaceForObject(value, %); - } - - public override ComInterfaceEntry* ComputeVtables(out int count) - { - count = sizeof(ReferenceInterfaceEntries) / sizeof(ComInterfaceEntry); - return (ComInterfaceEntry*)Unsafe.AsPointer(in %InterfaceEntriesImpl.Entries); - } - - public override object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) - { - wrapperFlags = CreatedWrapperFlags.NonWrapping; - return %; - } -} - -)", name, - use_tracker_object_support(type) ? "CreateComInterfaceFlags.TrackerSupport" : "CreateComInterfaceFlags.None", - name, - bind([&](writer& w) { - if (is_type_blittable(type)) - { - w.write("WindowsRuntimeValueTypeMarshaller.UnboxToManagedUnsafe<%>(value, in %)", - projection_name, - iid_property_name); - } - else - { - w.write("%Marshaller.ConvertToManaged(WindowsRuntimeValueTypeMarshaller.UnboxToManagedUnsafe<%>(value, in %))", - name, - abi_name, - iid_property_name); - } - })); - } - - void write_interface_entries_impl(writer& w, TypeDef const& type) - { - auto name = type.TypeName(); - - w.write( -R"(file static class %InterfaceEntriesImpl -{ - [FixedAddressValueType] - public static readonly ReferenceInterfaceEntries Entries; - - static %InterfaceEntriesImpl() - { - Entries.IReferenceValue.IID = %; - Entries.IReferenceValue.Vtable = %ReferenceImpl.Vtable; - Entries.IPropertyValue.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IPropertyValue; - Entries.IPropertyValue.Vtable = global::WindowsRuntime.InteropServices.IPropertyValueImpl.OtherTypeVtable; - Entries.IStringable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IStringable; - Entries.IStringable.Vtable = global::WindowsRuntime.InteropServices.IStringableImpl.Vtable; - Entries.IWeakReferenceSource.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IWeakReferenceSource; - Entries.IWeakReferenceSource.Vtable = global::WindowsRuntime.InteropServices.IWeakReferenceSourceImpl.Vtable; - Entries.IMarshal.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMarshal; - Entries.IMarshal.Vtable = global::WindowsRuntime.InteropServices.IMarshalImpl.Vtable; - Entries.IAgileObject.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IAgileObject; - Entries.IAgileObject.Vtable = global::WindowsRuntime.InteropServices.IAgileObjectImpl.Vtable; - Entries.IInspectable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IInspectable; - Entries.IInspectable.Vtable = global::WindowsRuntime.InteropServices.IInspectableImpl.Vtable; - Entries.IUnknown.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IUnknown; - Entries.IUnknown.Vtable = global::WindowsRuntime.InteropServices.IUnknownImpl.Vtable; - } -} - -)", name, name, bind(type), name); - } - - void write_delegates_interface_entries_impl(writer& w, TypeDef const& type) - { - auto name = type.TypeName(); - - w.write( - R"(file static class %InterfaceEntriesImpl -{ - [FixedAddressValueType] - public static readonly DelegateReferenceInterfaceEntries Entries; - - static %InterfaceEntriesImpl() - { - Entries.Delegate.IID = %; - Entries.Delegate.Vtable = %Impl.Vtable; - Entries.DelegateReference.IID = %; - Entries.DelegateReference.Vtable = %ReferenceImpl.Vtable; - Entries.IPropertyValue.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IPropertyValue; - Entries.IPropertyValue.Vtable = global::WindowsRuntime.InteropServices.IPropertyValueImpl.OtherTypeVtable; - Entries.IStringable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IStringable; - Entries.IStringable.Vtable = global::WindowsRuntime.InteropServices.IStringableImpl.Vtable; - Entries.IWeakReferenceSource.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IWeakReferenceSource; - Entries.IWeakReferenceSource.Vtable = global::WindowsRuntime.InteropServices.IWeakReferenceSourceImpl.Vtable; - Entries.IMarshal.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMarshal; - Entries.IMarshal.Vtable = global::WindowsRuntime.InteropServices.IMarshalImpl.Vtable; - Entries.IAgileObject.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IAgileObject; - Entries.IAgileObject.Vtable = global::WindowsRuntime.InteropServices.IAgileObjectImpl.Vtable; - Entries.IInspectable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IInspectable; - Entries.IInspectable.Vtable = global::WindowsRuntime.InteropServices.IInspectableImpl.Vtable; - Entries.IUnknown.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IUnknown; - Entries.IUnknown.Vtable = global::WindowsRuntime.InteropServices.IUnknownImpl.Vtable; - } -} - -)", name, name, bind(type), name, bind(type), name); - } - - - void write_pragma_restore_IL2026(writer& w) - { - w.write( -R"( -#pragma warning restore IL2026 -)"); - } - - void write_pragma_disable_IL2026(writer& w) - { - w.write( -R"( -#pragma warning disable IL2026 -)"); - } - - void write_winrt_windowsmetadata_typemapgroup_assembly_attribute(writer& w, TypeDef const& type) - { - // We don't need to emit it for exclusive interfaces or internal projections given - // they are projected as internal and xaml won't be using typeof with them. - if (get_category(type) == category::interface_type && - (is_exclusive_to(type) || is_projection_internal(type))) - { - return; - } - - auto projection_name = w.write_temp("%", bind(type, typedef_name_type::NonProjected, true)); - w.write( -R"( -[assembly: TypeMap( - value: "%", - target: typeof(%), - trimTarget: typeof(%))] -)", - projection_name, - bind([&](writer& w) { - if (settings.component) - { - write_type_name(w, type, typedef_name_type::ABI, true); - } - else - { - w.write("%", projection_name); - } - }), - projection_name); - - // Component types attributes are placed on a proxy type. - if (settings.component) - { - w.write( - R"( -[assembly: TypeMapAssociation( - source: typeof(%), - proxy: typeof(%))] - -)", - projection_name, - bind(type, typedef_name_type::ABI, true)); - } - } - - void write_winrt_comwrappers_typemapgroup_assembly_attribute(writer& w, TypeDef const& type, bool is_value_type) - { - auto projection_name = w.write_temp("%", bind(type, typedef_name_type::NonProjected, true)); - w.write( -R"( -[assembly: TypeMap( - value: "%", - target: typeof(%), - trimTarget: typeof(%))] -)", - bind([&](writer& w) { - if (is_value_type) - { - w.write("Windows.Foundation.IReference`1<%>", projection_name); - } - else - { - w.write(projection_name); - } - }), - bind([&](writer& w) { - if (settings.component) - { - write_type_name(w, type, typedef_name_type::ABI, true); - } - else - { - w.write("%", projection_name); - } - }), - projection_name); - - // For structs, enums, and delegates that are authored, - // we can't put the marshaler attribute on the actual type, - // so we use a proxy type. - if (get_category(type) != category::interface_type && - get_category(type) != category::struct_type && - settings.component) - { - w.write( - R"( -[assembly: TypeMapAssociation( - source: typeof(%), - proxy: typeof(%))] - -)", - projection_name, - bind(type, typedef_name_type::ABI, true)); - } - } - - void write_winrt_idic_typemapgroup_assembly_attribute(writer& w, TypeDef const& type) - { - // Generic interfaces are handled by cswinrtinteropgen. - if (distance(type.GenericParam()) != 0) - { - return; - } - - if ((is_exclusive_to(type) && !settings.idic_exclusiveto) || - is_projection_internal(type)) - { - return; - } - - w.write( - R"( -[assembly: TypeMapAssociation( - source: typeof(%), - proxy: typeof(%))] - -)", - bind(type, typedef_name_type::Projected, true), - bind(type, typedef_name_type::ABI, true)); - } - - void write_winrt_reference_type_attribute(writer& w, TypeDef const& type) - { - if (settings.reference_projection) - { - return; - } - - w.write("[WindowsRuntimeReferenceType(typeof(%?))]\n", - bind(type, typedef_name_type::Projected, false)); - } - - void write_winrt_metadata_attribute(writer& w, TypeDef const& type) - { - std::filesystem::path db_path(type.get_database().path()); - w.write("[WindowsRuntimeMetadata(\"%\")]\n", db_path.stem().string()); - } - - void write_winrt_metadata_typename_attribute(writer& w, TypeDef const& type) - { - w.write("[WindowsRuntimeMetadataTypeName(\"%\")]\n", - bind(type, typedef_name_type::NonProjected, true)); - } - - void write_winrt_mapped_type_attribute(writer& w, TypeDef const& type) - { - w.write("[WindowsRuntimeMappedType(typeof(%))]\n", - bind(type, typedef_name_type::Projected, true)); - } - - void write_value_type_winrt_classname_attribute(writer& w, TypeDef const& type) - { - if (settings.reference_projection) - { - return; - } - - w.write("[WindowsRuntimeClassName(\"Windows.Foundation.IReference`1<%.%>\")]\n", - type.TypeNamespace(), type.TypeName()); - } - - void write_comwrapper_marshaller_attribute(writer& w, TypeDef const& type) - { - if (settings.reference_projection) - { - return; - } - - w.write("[ABI.%.%ComWrappersMarshaller]\n", - type.TypeNamespace(), type.TypeName()); - } - - void add_default_interface_entry(writer& w, TypeDef const& type, - concurrency::concurrent_unordered_map& defaultInterfaceEntries) - { - if (settings.reference_projection) - { - return; - } - - auto default_interface = get_default_interface(type); - - if (!default_interface) - { - return; - } - - auto class_name = w.write_temp("global::%.@", type.TypeNamespace(), type.TypeName()); - - for_typedef(w, get_type_semantics(default_interface), [&](auto type) - { - auto interface_name = w.write_temp("%", bind(type, typedef_name_type::CCW, true)); - - defaultInterfaceEntries.insert({ std::move(class_name), std::move(interface_name) }); - }); - } - - void write_file_header(writer& w) - { - w.write(R"(//------------------------------------------------------------------------------ -// -// This file was generated by cswinrt.exe version % -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -)", VERSION_STRING); - } - - void write_default_interfaces_class( - std::vector> const& sortedEntries) - { - if (sortedEntries.empty()) - { - return; - } - - writer w; - write_file_header(w); - w.write(R"(using System; -using WindowsRuntime; - -#pragma warning disable CSWINRT3001 - -namespace ABI -{ -%internal static class WindowsRuntimeDefaultInterfaces; -} -)", - bind([&](writer& w) - { - for (auto& [class_name, interface_name] : sortedEntries) - { - w.write("[WindowsRuntimeDefaultInterface(typeof(%), typeof(%))]\n", - class_name, interface_name); - } - })); - - w.flush_to_file(settings.output_folder / "WindowsRuntimeDefaultInterfaces.cs"); - } - - void add_exclusive_to_interface_entries(writer& w, TypeDef const& type, - concurrency::concurrent_vector>& exclusiveToInterfaceEntries) - { - if (!settings.component || settings.reference_projection) - { - return; - } - - auto class_name = w.write_temp("global::%.@", type.TypeNamespace(), type.TypeName()); - - for (auto&& iface : type.InterfaceImpl()) - { - auto semantics = get_type_semantics(iface.Interface()); - - for_typedef(w, semantics, [&](auto iface_type) - { - if (is_exclusive_to(iface_type)) - { - auto interface_name = w.write_temp("%", bind(iface_type, typedef_name_type::CCW, true)); - exclusiveToInterfaceEntries.push_back({ class_name, std::move(interface_name) }); - } - }); - } - } - - void write_exclusive_to_interfaces_class( - std::vector> const& sortedEntries) - { - if (sortedEntries.empty()) - { - return; - } - - writer w; - write_file_header(w); - w.write(R"(using System; -using WindowsRuntime; - -#pragma warning disable CSWINRT3001 - -namespace ABI -{ -%internal static class WindowsRuntimeExclusiveToInterfaces; -} -)", - bind([&](writer& w) - { - for (auto& [class_name, interface_name] : sortedEntries) - { - w.write("[WindowsRuntimeExclusiveToInterface(typeof(%), typeof(%))]\n", - class_name, interface_name); - } - })); - - w.flush_to_file(settings.output_folder / "WindowsRuntimeExclusiveToInterfaces.cs"); - } - - void write_winrt_attribute(writer& w, TypeDef const& type) - { - std::filesystem::path db_path(type.get_database().path()); - if (get_category(type) == category::struct_type) - { - w.write(R"([global::WinRT.WindowsRuntimeType("%", "%")])", - db_path.stem().string(), - bind(type)); - } - else - { - w.write(R"([global::WinRT.WindowsRuntimeType("%")])", - db_path.stem().string()); - } - } - - void write_winrt_helper_type_attribute(writer& w, TypeDef const& type) - { - if (get_category(type) == category::struct_type && is_type_blittable(type)) - { - w.write(R"([global::WinRT.WindowsRuntimeHelperType])"); - return; - } - - w.write(R"([global::WinRT.WindowsRuntimeHelperType(typeof(%%))])", - bind(type, typedef_name_type::ABI, false), - bind([&](writer& w) - { - if (distance(type.GenericParam()) == 0) - { - return; - } - - // Writes out the generic definition without the types. - separator s{ w }; - w.write("<%>", bind_each([&](writer& /*w*/, GenericParam const& /*gp*/){ s(); }, type.GenericParam())); - })); - } - - // The WinRTExposedType attribute decides the interfaces that are - // placed on the vtable during CCW generation. - // - // Given structs and delegates can be boxed and passed as an object, - // we write this attribute to represent the interfaces they implement. - // - // For enums, they can also be boxed, but there are only two types of - // enums (int and uint) allowing us to handle this directly during CCW - // generation. - // - // For projection classes, they are typically just unwrapped to get the - // native object when passed across the ABI. The exception to this is - // unsealed classes which are extended by consumers in C# code. - // For these classes, we generate the attribute using the source generator - // and put it on the class extending it and also includes the - // override interfaces from the unsealed base class. - // - // For classes authored using our component authoring support, we - // generate the attribute on the Impl namespace metadata classes using the - // source generator rather than here as it allows us to better handle - // covariant interfaces. But for factory classes, we generate them here. - bool should_write_winrt_exposed_type_attribute(TypeDef const& type, bool isFactory) - { - if (settings.netstandard_compat) - { - return false; - } - - return isFactory || - get_category(type) == category::struct_type || - get_category(type) == category::enum_type || - (get_category(type) == category::delegate_type && distance(type.GenericParam()) == 0); - } - - void write_winrt_exposed_type_attribute(writer& w, TypeDef const& type, bool isFactory) - { - if (should_write_winrt_exposed_type_attribute(type, isFactory)) - { - if (get_category(type) == category::struct_type) - { - w.write(R"([global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails<%, %>))])", - bind(type, typedef_name_type::Projected, false), - bind(type, is_type_blittable(type) ? typedef_name_type::Projected : typedef_name_type::ABI, false)); - } - else if (get_category(type) == category::enum_type) - { - w.write(R"([global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails<%>))])", - bind(type, typedef_name_type::Projected, false)); - } - else - { - w.write(R"([global::WinRT.WinRTExposedType(typeof(%%WinRTTypeDetails))])", - bind(type, typedef_name_type::ABI, false), - isFactory ? "ServerActivationFactory" : ""); - } - } - } - - void write_winrt_exposed_type_class(writer& w, TypeDef const& type, bool isFactory) - { - if (should_write_winrt_exposed_type_attribute(type, isFactory)) - { - if (get_category(type) == category::class_type && isFactory) - { - w.write(R"( -internal sealed class %ServerActivationFactoryWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails -{ -public global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() -{ -return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] -{ -% -}; -} -} -)", - bind(type, typedef_name_type::ABI, false), - bind([&](writer& w) - { - w.write(R"( -new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry -{ -IID = global::ABI.WinRT.Interop.IActivationFactoryMethods.IID, -Vtable = global::ABI.WinRT.Interop.IActivationFactoryMethods.AbiToProjectionVftablePtr -}, -)"); - - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if ((factory.activatable || factory.statics) && factory.type) - { - w.write(R"( -new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry -{ -IID = %.IID, -Vtable = %.AbiToProjectionVftablePtr -}, -)", - bind(factory.type, typedef_name_type::StaticAbiClass, false), - // These are exclusive internal interfaces, so just use the ABI class to get the ptr. - bind(factory.type, typedef_name_type::ABI, false) - ); - } - } - }) - ); - } - else if (get_category(type) == category::delegate_type) - { - w.write(R"( -internal sealed class %WinRTTypeDetails : global::WinRT.DelegateTypeDetails<%> -{ -public override ComWrappers.ComInterfaceEntry GetDelegateInterface() -{ -return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry -{ -IID = %.IID, -Vtable = %.AbiToProjectionVftablePtr -}; -} -} -)", - bind(type, typedef_name_type::ABI, false), - bind(type, typedef_name_type::Projected, false), - bind(type, typedef_name_type::ABI, false), - bind(type, typedef_name_type::ABI, false) - ); - } - } - } - - auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& abi_methods_start_index = INSPECTABLE_METHOD_COUNT) - { - TypeDef const& type = method.Parent(); - return std::pair{ - w.write_temp("(*(delegate* unmanaged[MemberFunction]<%, int>**)ThisPtr)[%]", - bind(method_signature { method }), - get_vmethod_index(type, method) + abi_methods_start_index /* number of methods in IInspectable + previous methods if fastabi*/), - false - }; - }; - - void write_static_class(writer& w, TypeDef const& type) - { - w.write(R"(%%% static class % -{ -%} -)", - bind(type), - bind(type, true), - internal_accessibility(), - bind(type, typedef_name_type::Projected, false), - bind(type) - ); - } - - // Checks if any of the generic args in the concrete type is an non instantiated type. - // i.e. It is in an generic interface and uses the generic to define the type. - bool has_generic_param_in_concrete_type(generic_type_instance const& type) - { - for (size_t idx = 0; idx < type.generic_args.size(); idx++) - { - auto& generic_arg_semantic = type.generic_args[idx]; - if (auto gtp = std::get_if(&generic_arg_semantic)) - { - return true; - } - } - - return false; - } - - void write_ensure_generic_type_initialized_for_instance(writer& w, generic_type_instance const& instance, bool assign_to_variable = false) - { - if (settings.netstandard_compat) - { - return; - } - - auto concrete_generic_type = ConvertGenericTypeInstanceToConcreteType(w, instance); - if (has_generic_param_in_concrete_type(concrete_generic_type)) - { - return; - } - - auto guard{ w.push_generic_args(concrete_generic_type) }; - auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, concrete_generic_type.generic_type); - generic_type_instances.insert( - generic_type_instantiation - { - concrete_generic_type, - generic_instantiation_class_name - }); - - if (assign_to_variable) - { - w.write("private static readonly bool initialized = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();", generic_instantiation_class_name); - } - else - { - w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();", generic_instantiation_class_name); - } - } - - void write_ensure_generic_type_initialized(writer& w, cswinrt::type_semantics const& semantics, bool assign_to_variable = false) - { - call(semantics, - [&](generic_type_instance const& instance) - { - write_ensure_generic_type_initialized_for_instance(w, instance, assign_to_variable); - }, - [&](auto const&) {}); - } - - void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - - void write_event_source_ctor(writer& w, Event const& evt) - { - auto semantics = get_type_semantics(evt.EventType()); - if (!std::holds_alternative(semantics)) - { - return; - } - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.Constructor)] -[return: UnsafeAccessorType("%, WinRT.Interop")] -static extern object ctor(WindowsRuntimeObjectReference nativeObjectReference, int index); -)", -bind(semantics, typedef_name_type::EventSource) -); - } - - void write_event_source_ctor_call(writer& w, Event const& evt, uint32_t const& abi_methods_start_index = 6) - { - auto semantics = get_type_semantics(evt.EventType()); - auto [add, _] = get_event_methods(evt); - auto index = get_vmethod_index(add.Parent(), add) + abi_methods_start_index; - if (std::holds_alternative(semantics)) - { - w.write("Unsafe.As<%>(ctor(thisReference, %))", - bind(semantics, typedef_name_type::EventSource, false), - index); - } - else - { - w.write("new %(thisReference, %)", - bind(semantics, typedef_name_type::EventSource, false), - index); - } - } - - void write_event_sources(writer& w, TypeDef const& type) - { - for (auto&& evt : type.EventList()) - { - w.write(R"( -private global::ABI.WinRT.Interop.EventSource<%> _%;)", -bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), -evt.Name()); - } - } - - void write_event_source_table(writer& w, Event const& evt) - { - w.write(R"( -private static ConditionalWeakTable _% -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - [MethodImpl(MethodImplOptions.NoInlining)] - static ConditionalWeakTable MakeTable() - { - _ = global::System.Threading.Interlocked.CompareExchange(ref field, [], null); - - return global::System.Threading.Volatile.Read(in field); - } - - return global::System.Threading.Volatile.Read(in field) ?? MakeTable(); - } -} -)", - bind(get_type_semantics(evt.EventType()), typedef_name_type::EventSource, false), - evt.Name(), - bind(get_type_semantics(evt.EventType()), typedef_name_type::EventSource, false) -); - } - - void write_interface_member_signatures(writer& w, TypeDef const& type) - { - for (auto&& method : type.MethodList()) - { - if (is_special(method)) - { - continue; - } - - method_signature signature{ method }; - w.write(R"( -%% %(%);)", - bind(method.CustomAttribute(), false), - bind(signature), - method.Name(), - bind_list(", ", signature.params()) - ); - } - - for (auto&& prop : type.PropertyList()) - { - auto [getter, setter] = get_property_methods(prop); - // "new" required if overriding a getter in a base interface - auto new_keyword = (!getter && setter && find_property_interface(w, type, prop.Name()).second) ? "new " : ""; - w.write(R"( -%% % {%% })", - new_keyword, - write_prop_type(w, prop), - prop.Name(), - getter || setter ? " get;" : "", - setter ? " set;" : "" - ); - } - - for (auto&& evt : type.EventList()) - { - w.write(R"( -event % %;)", - bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), - evt.Name()); - } - } - - struct abi_marshaler - { - std::string param_name; - int param_index; - param_category category; - bool is_return; - std::string param_type; - std::string local_type; - std::string marshaler_type; - bool is_value_type; - bool is_pinnable; - bool marshal_by_object_reference_value; - bool has_generic_instantiation; - std::vector generic_instantiations; - std::string interface_init_rcw_helper; - std::string interop_dll_type; - bool skip_disposer; - bool is_boxed_value; - - bool is_out() const - { - return (category == param_category::out) || - (category == param_category::receive_array); - } - - bool is_value_type_in() const - { - return is_const_ref() && !marshaler_type.empty(); - } - - bool is_ref() const - { - return (category == param_category::fill_array); - } - - bool is_generic() const - { - return param_index > -1; - } - - bool is_array() const - { - return category >= param_category::pass_array; - } - - bool is_object_in() const - { - return ((category == param_category::in) || (category == param_category::ref)) && - marshaler_type.empty() && local_type == "void*"; - } - - bool is_pinnable_array_data() const - { - return is_array() && !is_out(); - } - - // We pass using in for .NET Standard. Outside of .NET Standard, - // we want our function pointers to be blittable and be able to disable - // runtime marshaling, so we use ptrs with the managed function calling the - // function pointer marking the parameter as in. - bool is_const_ref() const - { - return category == param_category::ref; - } - - bool is_marshal_by_object_reference_value() const - { - return marshal_by_object_reference_value; - } - - std::string get_marshaler_local(writer& w) const - { - return w.write_temp("__%", param_name); - } - - std::string get_param_local(writer& w) const - { - if (!is_generic()) - { - return is_array() ? - w.write_temp("__%_length, __%_data", - param_name, param_name) : - get_marshaler_local(w); - } - return is_array() ? - w.write_temp("(__params[%], __params[%])", - param_index, param_index + 1) : - w.write_temp("__params[%]", param_index); - } - - std::string get_inline_array_type() const - { - if (local_type == "void*") - { - return "nint"; - } - else - { - return local_type; - } - } - - void write_convert_to_managed_function(writer& w) const - { - if (interop_dll_type != "") - { - if (is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] -static extern %[] ConvertToManaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, uint length, %* data); - -)", -param_type, -param_name, -interop_dll_type, -local_type); - } - else - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] -static extern % ConvertToManaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, void* value); - -)", -param_type, -param_name, -interop_dll_type); - } - } - } - - void write_interop_dispose_function(writer& w) const - { - if (interop_dll_type != "" && is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] -static extern void Dispose_%([UnsafeAccessorType("%, WinRT.Interop")] object _, uint length, %* data); - -)", -param_name, -interop_dll_type, -local_type); - } - } - - void write_interop_free_function(writer& w) const - { - if (interop_dll_type != "" && is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Free")] -static extern void Free_%([UnsafeAccessorType("%, WinRT.Interop")] object _, uint length, %* data); - -)", -param_name, -interop_dll_type, -local_type); - } - } - - void write_convert_to_unmanaged_function(writer& w) const - { - if (interop_dll_type != "") - { - if (is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern void ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, ReadOnlySpan<%> span, out uint length, out %* data); - -)", -param_name, -interop_dll_type, -param_type, -local_type); - } - else - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, % value); - -)", -param_name, -interop_dll_type, -param_type); - } - } - } - - void write_copy_to_unmanaged_function(writer& w) const - { - if (is_array() && marshaler_type == "HStringMarshaller") - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanagedUnsafe")] -static extern void CopyToUnmanagedUnsafe_%([UnsafeAccessorType("ABI.System.ArrayMarshaller, WinRT.Interop")] object _, ReadOnlySpan value, uint destinationSize, void* destination, uint headersSize, void* headers); - -)", -param_name); - } - else if (interop_dll_type != "") - { - if (is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] -static extern void CopyToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, ReadOnlySpan<%> span, uint length, %* data); - -)", -param_name, -interop_dll_type, -param_type, -local_type); - } - } - } - - void write_copy_to_managed_function(writer& w) const - { - if (interop_dll_type != "") - { - if (is_ref()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] -static extern void CopyToManaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, uint length, %* data, Span<%> span); - -)", -param_name, -interop_dll_type, -local_type, -param_type); - } - } - } - - void write_locals(writer& w) const - { - if (is_pinnable) - return; - - if (is_object_in() || local_type.empty()) - return; - - if (is_array()) - { - if (is_out()) - { - w.write("uint __%_length = default;\n", param_name); - w.write("%* __%_data = default;\n", local_type, param_name); - } - else - { - w.write(R"( -Unsafe.SkipInit(out InlineArray16<%> __%_inlineArray); -%[] __%_arrayFromPool = null; -Span<%> __%_span = %.Length <= 16 - ? __%_inlineArray[..%.Length] - : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent(%.Length)); -)", - // SkipInit - get_inline_array_type(), - param_name, - // array - get_inline_array_type(), - param_name, - // span - get_inline_array_type(), - param_name, - get_escaped_param_name(w), - param_name, - get_escaped_param_name(w), - param_name, - get_inline_array_type(), - get_escaped_param_name(w)); - - if (!is_ref() && marshaler_type == "HStringMarshaller") - { - w.write(R"( -Unsafe.SkipInit(out InlineArray16 __%_inlineHeaderArray); -HStringHeader[] __%_headerArrayFromPool = null; -Span __%_headerSpan = %.Length <= 16 - ? __%_inlineHeaderArray[..%.Length] - : (__%_headerArrayFromPool = global::System.Buffers.ArrayPool.Shared.Rent(%.Length)); - -Unsafe.SkipInit(out InlineArray16 __%_inlinePinnedHandleArray); -nint[] __%_pinnedHandleArrayFromPool = null; -Span __%_pinnedHandleSpan = %.Length <= 16 - ? __%_inlinePinnedHandleArray[..%.Length] - : (__%_pinnedHandleArrayFromPool = global::System.Buffers.ArrayPool.Shared.Rent(%.Length)); -)", - // inlineHeaderArray - // SkipInit - param_name, - // array - param_name, - // span - param_name, - get_escaped_param_name(w), - param_name, - get_escaped_param_name(w), - param_name, - get_escaped_param_name(w), - // inlinePinnedHandleArray - // SkipInit - param_name, - // array - param_name, - // span - param_name, - get_escaped_param_name(w), - param_name, - get_escaped_param_name(w), - param_name, - get_escaped_param_name(w)); - } - } - return; - } - - // If we are skipping the disposer, it implies there is a marshaler. - // It also means we won't handle it as part of writing assignments. - if (!is_out() && skip_disposer) - { - w.write("% % = %.ConvertToUnmanaged(%);\n", - local_type, - get_marshaler_local(w), - marshaler_type, - get_escaped_param_name(w)); - return; - } - - if (is_out() || !is_marshal_by_object_reference_value()) - { - w.write("% __% = default;\n", - local_type, - param_name); - return; - } - - write_convert_to_unmanaged_function(w); - - if (is_boxed_value) - { - w.write("using WindowsRuntimeObjectReferenceValue % = %.BoxToUnmanaged(%);\n", - get_marshaler_local(w), - marshaler_type, - get_escaped_param_name(w)); - return; - } - - w.write("using WindowsRuntimeObjectReferenceValue % = %ConvertToUnmanaged%(%%);\n", - get_marshaler_local(w), - interop_dll_type != "" ? "" : marshaler_type + ".", - interop_dll_type != "" ? "_" + param_name : "", - interop_dll_type != "" ? "null, " : "", - get_escaped_param_name(w)); - } - - auto get_escaped_param_name(writer& w) const - { - return w.write_temp("%", bind(param_name)); - } - - void write_assignments(writer& w) const - { - if (is_pinnable || is_pinnable_array_data() || is_object_in() || is_out() || local_type.empty()) - return; - - if (!marshaler_type.empty() && !is_marshal_by_object_reference_value()) - { - w.write("% = %.ConvertToUnmanaged(%);\n", - get_marshaler_local(w), - marshaler_type, - get_escaped_param_name(w)); - } - } - - void write_pinnable(writer& w) const - { - if (!is_pinnable || marshaler_type != "global::ABI.System.TypeMarshaller") - return; - - w.write("global::ABI.System.TypeMarshaller.ConvertToUnmanagedUnsafe(%, out TypeReference __%);\n", - get_escaped_param_name(w), param_name); - } - - void write_fixed_expression(writer& w, bool& write_delimiter) const - { - if (!is_pinnable && !is_pinnable_array_data()) - return; - if (write_delimiter) - { - w.write(", "); - } - - if (!is_pinnable && is_pinnable_array_data()) - { - w.write("_% = __%_span", - param_name, - param_name); - - if (!is_ref() && marshaler_type == "HStringMarshaller") - { - w.write(", _%_inlineHeaderArray = __%_headerSpan", - param_name, - param_name); - } - } - else - { - w.write("_% = %", - param_name, - marshaler_type == "global::ABI.System.TypeMarshaller" - ? "__" + param_name : get_escaped_param_name(w)); - } - write_delimiter = true; - } - - void write_fixed_marshaler(writer& w) const - { - if (!is_pinnable && !is_pinnable_array_data()) - return; - - if (marshaler_type == "HStringMarshaller") - { - if (is_pinnable_array_data()) - { - if (is_ref()) - { - return; - } - - w.write(R"( -HStringArrayMarshaller.ConvertToUnmanagedUnsafe( - source: %, - hstringHeaders: (HStringHeader*) _%_inlineHeaderArray, - hstrings: __%_span, - pinnedGCHandles: __%_pinnedHandleSpan); -)", - get_escaped_param_name(w), - param_name, - param_name, - param_name); - } - else - { - w.write("HStringMarshaller.ConvertToUnmanagedUnsafe((char*)_%, %?.Length, out HStringReference __%);", - param_name, get_escaped_param_name(w), param_name); - } - } - else if (!is_pinnable && is_pinnable_array_data() && !is_ref()) - { - write_copy_to_unmanaged_function(w); - w.write("CopyToUnmanaged_%(null, %, (uint)%.Length, (%*)_%);\n", - param_name, - get_escaped_param_name(w), - get_escaped_param_name(w), - local_type, - param_name); - } - } - - void write_marshal_to_abi(writer& w, std::string_view source = "") const - { - if (!is_generic()) - { - if (is_array()) - { - if (is_pinnable || is_pinnable_array_data()) - { - w.write("(uint) %.Length, _%", get_escaped_param_name(w), param_name); - } - else - { - w.write("%__%_length, %__%_data", - is_out() ? "&" : "", param_name, - is_out() ? "&" : "", param_name); - } - return; - } - - if (is_out() || is_value_type_in()) - { - w.write("&__%", param_name); - return; - } - - if (is_object_in()) - { - w.write("%%.GetThisPtrUnsafe()", source, bind(param_name)); - return; - } - - if (marshaler_type.empty()) - { - w.write("%%%", - category == param_category::ref ? "_" : "", - source, bind(param_name)); - return; - } - - if (is_pinnable) - { - if (marshaler_type == "HStringMarshaller") - { - w.write("%.HString", - get_marshaler_local(w)); - return; - } - else if (marshaler_type == "global::ABI.System.TypeMarshaller") - { - w.write("%.ConvertToUnmanagedUnsafe()", - get_marshaler_local(w)); - return; - } - } - - if (!is_marshal_by_object_reference_value()) - { - w.write("%", - get_marshaler_local(w)); - return; - } - } - - if (is_array()) - { - w.write("__%_length, __%_data", - param_name, - param_name); - return; - } - - if (marshaler_type.empty()) - { - write_escaped_identifier(w, param_name); - return; - } - - if (is_const_ref()) - { - w.write("&____%", param_name); - return; - } - - w.write("%.GetThisPtrUnsafe()", - get_marshaler_local(w)); - } - - void write_from_abi(writer& w, std::string_view source) const - { - auto param_cast = is_generic() ? - w.write_temp("(%)", param_type) : ""; - - if (marshaler_type.empty()) - { - if (local_type == "void*" && param_type != "void*") - { - w.write("%.FromAbi(%)", param_type, source); - return; - } - w.write("%%", param_cast, source); - return; - } - - if (is_boxed_value) - { - w.write("%.UnboxToManaged(%)", - marshaler_type, - source); - return; - } - - w.write("%ConvertToManaged%(%%)", - interop_dll_type != "" ? "" : marshaler_type + ".", - interop_dll_type != "" ? "_" + param_name : "", - is_generic() || interop_dll_type != "" ? "null, " : "", - source); - } - - void write_from_managed(writer& w, std::string_view source) const - { - auto param_cast = is_generic() ? - w.write_temp("(%)", param_type) : ""; - - if (marshaler_type.empty()) - { - if (local_type == "void*") - { - w.write("%.FromManaged(%)", param_type, source); - return; - } - w.write("%%", param_cast, source); - return; - } - - w.write("%.FromManaged%(%)", - marshaler_type, - is_array() ? "Array" : "", - source); - } - - void write_marshal_from_abi(writer& w) const - { - if (!is_ref() && (!is_out() || local_type.empty())) - return; - if (is_ref()) - { - if (!is_pinnable) - { - write_copy_to_managed_function(w); - w.write("CopyToManaged_%(null, (uint)__%_span.Length, (%*)_%, %);\n", - param_name, - param_name, - local_type, - param_name, - bind(param_name)); - } - return; - } - - write_convert_to_managed_function(w); - - is_return ? - w.write("return ") : - w.write("% = ", bind(param_name)); - write_from_abi(w, get_param_local(w)); - w.write(";\n"); - } - - void write_dispose(writer& w) const - { - if (is_pinnable || is_object_in() || local_type.empty() || (!is_out() && is_marshal_by_object_reference_value()) || skip_disposer) - return; - - if (marshaler_type.empty()) - { - if (is_out() && (local_type == "void*" && param_type != "void*")) - { - w.write("WindowsRuntimeUnknownMarshaller.Free(%);\n", get_marshaler_local(w)); - } - return; - } - - if (is_out()) - { - if (is_array()) - { - write_interop_free_function(w); - - w.write("Free_%(null, %);\n", - param_name, - get_param_local(w)); - } - else if (is_marshal_by_object_reference_value()) - { - w.write("WindowsRuntimeUnknownMarshaller.Free(%);\n", - get_param_local(w)); - } - else if (marshaler_type == "HStringMarshaller") - { - w.write("%.Free(%);\n", - marshaler_type, - get_param_local(w)); - } - else - { - w.write("%.Dispose(%);\n", - marshaler_type, - get_param_local(w)); - } - } - else - { - if (is_array()) - { - if (category == param_category::pass_array && - marshaler_type == "HStringMarshaller") - { - w.write(R"( -HStringArrayMarshaller.Dispose(__%_pinnedHandleSpan); - -if (__%_pinnedHandleArrayFromPool is not null) -{ -global::System.Buffers.ArrayPool.Shared.Return(__%_pinnedHandleArrayFromPool); -} - -if (__%_headerArrayFromPool is not null) -{ -global::System.Buffers.ArrayPool.Shared.Return(__%_headerArrayFromPool); -} -)", - param_name, - param_name, - param_name, - param_name, - param_name); - } - // We need to dispose when it is fill array including for strings. - // We also need to dispose for pass arrays of other types that has a disposer. - else if (!skip_disposer) - { - write_interop_dispose_function(w); - - w.write(R"( -fixed(%% _% = __%_span) -{ -Dispose_%(null, (uint) __%_span.Length, %_%); -} -)", - local_type, - // If it is already a pointer type, - // we can pass the pointer from the fixed expression. - // And this allows us to handle that the span is of type nint. - local_type == "void*" ? "" : "*", - param_name, - param_name, - param_name, - param_name, - bind([&](writer& w) - { - if (local_type == "void*") - { - w.write("(void**)"); - } - }), - param_name); - } - - w.write(R"( -if (__%_arrayFromPool is not null) -{ -global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); -} -)", - param_name, - get_inline_array_type(), - param_name); - } - else - { - w.write("%.Dispose(%);\n", - marshaler_type, - get_param_local(w)); - } - } - } - }; - - void set_abi_marshaler(writer& w, TypeSig const& type_sig, abi_marshaler& m, std::string_view prop_name = "", bool is_generic_instantiation_class = false) - { - auto semantics = get_type_semantics(type_sig); - m.param_type = w.write_temp("%", bind(semantics)); - m.is_value_type = is_value_type(semantics); - - auto get_abi_type = [&]() - { - auto abi_type = w.write_temp("%", bind(semantics, typedef_name_type::ABI, false)); - if (abi_type != prop_name) - { - return abi_type; - } - return w.write_temp("%", bind(semantics, typedef_name_type::ABI, true)); - }; - - auto set_skip_disposer_if_needed = [&](abi_marshaler& m) - { - // These are types which are custom mapped and don't have disposers. - if (m.local_type == "global::ABI.System.DateTimeOffset" || - m.local_type == "global::ABI.System.TimeSpan" || - m.local_type == "global::ABI.System.Exception") - { - m.skip_disposer = true; - } - }; - - auto set_simple_marshaler_type = [&](abi_marshaler& m, TypeDef const& type) - { - if (m.is_array()) - { - m.is_pinnable = is_type_blittable(semantics) && !m.is_out(); - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.marshaler_type += "<" + m.param_type + ">"; - m.local_type = w.write_temp("%", bind(semantics)); - set_skip_disposer_if_needed(m); - } - else if (!is_type_blittable(type)) - { - m.local_type = get_abi_type(); - m.marshaler_type = m.local_type + "Marshaller"; - - auto abi_type = w.write_temp("%", bind(semantics, typedef_name_type::ABI, true)); - if (m.marshaler_type == "global::ABI.System.TypeMarshaller") - { - m.is_pinnable = (m.category == param_category::in); - } - else - { - set_skip_disposer_if_needed(m); - } - } - }; - - auto set_typedef_marshaler = [&](abi_marshaler& m, TypeDef const& type) - { - switch (get_category(type)) - { - case category::enum_type: - break; - case category::struct_type: - set_simple_marshaler_type(m, type); - break; - case category::interface_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - if (m.is_array()) - { - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.local_type = w.write_temp("%", bind(semantics)); - } - else - { - m.marshal_by_object_reference_value = true; - m.local_type = m.is_out() ? "void*" : "ObjectReferenceValue"; - - if (is_projected_as_nullable(type)) - { - m.is_boxed_value = true; - auto generic_arg = w.get_generic_arg(0); - call(generic_arg, - [&](guid_type) - { - m.marshaler_type = "ABI.System.GuidMarshaller"; - }, - [&](type_type) - { - m.marshaler_type = "ABI.System.TypeMarshaller"; - }, - [&](type_definition const& type) { - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - }, - [&](fundamental_type const& type) - { - m.marshaler_type = w.write_temp("ABI.System.%Marshaller", bind(type)); - }, - [&](auto const&) {}); - } - else if (distance(type.GenericParam()) > 0) - { - m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); - } - } - - break; - case category::class_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); - if (m.is_array()) - { - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.local_type = "void*"; - } - else - { - m.marshal_by_object_reference_value = true; - m.local_type = m.is_out() ? "void*" : "ObjectReferenceValue"; - } - break; - case category::delegate_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); - if (m.is_array()) - { - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.local_type = "void*"; - } - else - { - m.marshal_by_object_reference_value = true; - m.local_type = m.is_out() ? "void*" : "ObjectReferenceValue"; - if (distance(type.GenericParam()) > 0) - { - m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); - } - } - break; - } - }; - - std::function set_type_semantics_marshaler = [&]() - { - call(semantics, - [&](object_type) - { - m.marshaler_type = "WindowsRuntimeObjectMarshaller"; - if (m.is_array()) - { - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.local_type = "void*"; - } - else - { - m.marshal_by_object_reference_value = true; - m.local_type = m.is_out() ? "void*" : "ObjectReferenceValue"; - } - }, - [&](type_definition const& type) - { - set_typedef_marshaler(m, type); - }, - [&](generic_type_index const& var) - { - if (is_generic_instantiation_class) - { - semantics = w.get_generic_arg(var.index); - set_type_semantics_marshaler(); - } - else - { - m.param_type = w.write_temp("%", bind(semantics)); - m.marshaler_type = w.write_temp("Marshaler<%>", m.param_type); - // In our non netstandard projection, this should only occur in our generic instantiated - // static method classes which takes the ABI type as a generic. - m.local_type = !settings.netstandard_compat && m.is_out() ? w.write_temp("%Abi", m.param_type) : "object"; - } - }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - set_typedef_marshaler(m, type.generic_type); - }, - [&](fundamental_type type) - { - if (type == fundamental_type::String) - { - if (m.is_array()) - { - m.marshaler_type = "HStringMarshaller"; - m.local_type = "void*"; - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - // m.is_pinnable = !m.is_out(); - } - else - { - m.marshaler_type = "HStringMarshaller"; - m.local_type = m.is_out() ? "void*" : "MarshalString"; - m.is_pinnable = (m.category == param_category::in); - } - } - }, - [&](auto const&) {}); - }; - set_type_semantics_marshaler(); - - if (m.is_out() && m.local_type.empty()) - { - m.local_type = w.write_temp("%", bind(semantics)); - } - - if (m.is_array() && m.marshaler_type.empty()) - { - if (m.is_generic()) - { - m.marshaler_type = w.write_temp("Marshaler<%>", m.param_type); - m.local_type = "object"; - } - else - { - m.is_pinnable = is_type_blittable(semantics) && !m.is_out(); - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.marshaler_type = is_type_blittable(semantics) ? "MarshalBlittable" : "MarshalNonBlittable"; - m.marshaler_type += "<" + m.param_type + ">"; - m.local_type = w.write_temp("%", bind(semantics)); - } - } - } - - auto get_abi_marshalers(writer& w, method_signature const& signature, bool is_generic, std::string_view prop_name = "", bool raw_return_type = false, bool is_generic_instantiation_class = false) - { - std::vector marshalers; - int param_index = 1; - - for (auto&& param : signature.params()) - { - abi_marshaler m{ - std::string(param.first.Name()), - is_generic ? param_index : -1, - get_param_category(param) - }; - param_index += m.is_array() ? 2 : 1; - set_abi_marshaler(w, param.second->Type(), m, prop_name, is_generic_instantiation_class); - marshalers.push_back(std::move(m)); - } - - if (auto ret = signature.return_signature()) - { - abi_marshaler m{ - "retval", - is_generic ? param_index : -1, - ret.Type().is_szarray() && !raw_return_type ? param_category::receive_array : param_category::out, - true - }; - param_index += m.is_array() ? 2 : 1; - if (!raw_return_type) - { - set_abi_marshaler(w, ret.Type(), m, prop_name, is_generic_instantiation_class); - } - else - { - m.param_type = w.write_temp("%", bind(get_type_semantics(ret.Type()))); - m.local_type = m.param_type; - m.is_value_type = true; - } - marshalers.push_back(std::move(m)); - } - - return marshalers; - } - - void write_abi_method_call_marshalers(writer& w, std::string_view invoke_target, std::string_view /*invoke_objref*/, bool /*is_generic*/, std::vector const& marshalers, bool has_noexcept_attr = false) - { - auto write_abi_invoke = [&](writer& w) - { - // Write out initial using statements marshaling the applicable parameters. - bool have_pinnables{}; - for (auto&& m : marshalers) - { - have_pinnables |= m.is_pinnable || m.is_pinnable_array_data(); - m.write_pinnable(w); - } - - // Write out of the fixed expression. - w.write("%", - bind_each([&](writer& w, abi_marshaler const& m) - { - if (m.is_const_ref() && m.marshaler_type.empty()) - { - w.write("fixed(%* _% = &%)\n", - m.param_type, - m.param_name, - m.param_name); - } - }, marshalers)); - - if (have_pinnables) - { - bool write_delimiter{}; - w.write("fixed(void* %)\n{\n", - bind_each([&](writer& w, abi_marshaler const& m) - { - m.write_fixed_expression(w, write_delimiter); - }, marshalers)); - - w.write("%\n", - bind_each([&](writer& w, abi_marshaler const& m) - { - m.write_fixed_marshaler(w); - }, marshalers)); - - // TODO: Write out the marshalers for the pinned expressions. - } - - if (!has_noexcept_attr) - { - w.write("RestrictedErrorInfo.ThrowExceptionForHR(%(ThisPtr%));\n", - invoke_target, - bind_each([](writer& w, abi_marshaler const& m) - { - w.write(",\n "); - m.write_marshal_to_abi(w); - }, marshalers)); - } - else { - w.write("%(ThisPtr%);\n", // TODO: rename to camelcase thisPtr - invoke_target, - bind_each([](writer& w, abi_marshaler const& m) - { - w.write(",\n "); - m.write_marshal_to_abi(w); - }, marshalers)); - } - for (auto&& m : marshalers) - { - m.write_marshal_from_abi(w); - } - if (have_pinnables) - { - w.write("}\n"); - } - }; - - w.write("\n"); - for (auto&& m : marshalers) - { - m.write_locals(w); - } - - bool have_disposers = std::find_if(marshalers.begin(), marshalers.end(), [](abi_marshaler const& m) - { - return !m.marshaler_type.empty() && !m.skip_disposer && (m.is_out() || (!m.is_pinnable && !m.is_marshal_by_object_reference_value())); - }) != marshalers.end(); - - if (!have_disposers) - { - write_abi_invoke(w); - return; - } - - w.write(R"(try -{ -%%} -finally -{ -%} -)", - bind_each([](writer& w, abi_marshaler const& m) - { - m.write_assignments(w); - }, marshalers), - bind(write_abi_invoke), - bind_each([](writer& w, abi_marshaler const& m) - { - m.write_dispose(w); - }, marshalers) - ); - } - - void write_abi_method_call(writer& w, method_signature signature, std::string_view invoke_target, std::string_view invoke_objref, bool is_generic, bool raw_return_type = false, bool has_noexcept_attr = false, bool is_generic_instantiation_class = false) - { - write_abi_method_call_marshalers(w, invoke_target, invoke_objref, is_generic, get_abi_marshalers(w, signature, is_generic, "", raw_return_type, is_generic_instantiation_class), has_noexcept_attr); - } - - void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method) - { - if (is_special(method)) - { - return; - } - - auto cache_object = bind(iface); - - method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method); - - auto abi_marshalers = get_abi_marshalers(w, signature, is_generic, "", true); - // The last abi marshaler is the return value which we want to treat as an out. - abi_marshalers[abi_marshalers.size() - 1].is_return = false; - - auto callback_class = w.write_temp("%", bind(method)); - auto args_struct = w.write_temp("%", bind(method)); - - // Generate the args struct for this activation factory callback, if the constructor has parameters. - if (!signature.params().empty()) - { - w.write(R"( -private readonly ref struct %(%) -{ -%} -)", - args_struct, - bind_list(", ", signature.params()), - bind_each([](writer& w, method_signature::param_t const& param) { - w.write("public readonly % % = %;\n", - bind(param), - bind(param), - bind(param)); - }, signature.params())); - } - - w.write(R"( -private sealed class % : WindowsRuntimeActivationFactoryCallback.DerivedSealed -{ -public static readonly % Instance = new(); - -[MethodImpl(MethodImplOptions.NoInlining)] -public override unsafe void Invoke( - WindowsRuntimeActivationArgsReference additionalParameters, - out void* retval) -{ -% -} -} -)", - callback_class, - callback_class, - bind([&](writer& w) { - if (settings.reference_projection) - { - w.write("throw null;"); - } - else - { - w.write(R"( -using WindowsRuntimeObjectReferenceValue activationFactoryValue = %.AsValue(); -void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); - -% -% -)", - cache_object, - bind([&](writer& w) { - auto const& params = signature.params(); - if (!params.empty()) - { - w.write("ref readonly % args = ref additionalParameters.GetValueRefUnsafe<%>();\n", - args_struct, args_struct); - for (size_t i = 0; i < params.size(); i++) - { - w.write("% % = args.%;\n", - bind(params[i]), - bind(params[i]), - bind(params[i])); - } - } - }), - bind(invoke_target, "", is_generic, abi_marshalers, is_noexcept(method))); - } - })); - } - - - void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method) - { - if (is_special(method) || settings.reference_projection) - { - return; - } - - auto cache_object = bind(iface); - - method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method); - - auto abi_marshalers = get_abi_marshalers(w, signature, is_generic, "", true); - // The last abi marshaler is the return value and the second-to-last one - // is the inner object (which is the return value we want). - size_t inner_inspectable_index = abi_marshalers.size() - 2; - abi_marshaler const& inner_inspectable_ref = abi_marshalers[inner_inspectable_index]; - abi_marshalers[inner_inspectable_index] = { - inner_inspectable_ref.param_name, - inner_inspectable_ref.param_index, - inner_inspectable_ref.category, - inner_inspectable_ref.is_return, - "void*", - "void*", - {}, - true - }; - // The last abi marshaler is the return value which we want to treat as an out. - abi_marshalers[inner_inspectable_index + 1].is_return = false; - - auto callback_class = w.write_temp("%", bind(method)); - auto args_struct = w.write_temp("%", bind(method)); - auto const& all_params = signature.params(); - size_t user_param_count = all_params.size() - 2; - - // Generate the args struct for this activation factory callback. - // This is only called when there are user parameters (i.e. params beyond the last 2 composition params). - w.write(R"( -private readonly ref struct %(%) -{ -%} -)", - args_struct, - bind([&](writer& w) { - for (size_t i = 0; i < user_param_count; i++) - { - if (i > 0) w.write(", "); - write_projection_parameter(w, all_params[i]); - } - }), - bind([&](writer& w) { - for (size_t i = 0; i < user_param_count; i++) - { - w.write("public readonly % % = %;\n", - bind(all_params[i]), - bind(all_params[i]), - bind(all_params[i])); - } - })); - - w.write(R"( -private sealed class % : WindowsRuntimeActivationFactoryCallback.DerivedComposed -{ -public static readonly % Instance = new(); - -[MethodImpl(MethodImplOptions.NoInlining)] -public override unsafe void Invoke( - WindowsRuntimeActivationArgsReference additionalParameters, - WindowsRuntimeObject baseInterface, - out void* innerInterface, - out void* retval) -{ -% -} -} -)", - callback_class, - callback_class, - bind([&](writer& w) { - w.write(R"( -using WindowsRuntimeObjectReferenceValue activationFactoryValue = %.AsValue(); -void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); - -% -% -)", - cache_object, - bind([&](writer& w) { - w.write("ref readonly % args = ref additionalParameters.GetValueRefUnsafe<%>();\n", - args_struct, args_struct); - for (size_t i = 0; i < user_param_count; i++) - { - w.write("% % = args.%;\n", - bind(all_params[i]), - bind(all_params[i]), - bind(all_params[i])); - } - }), - bind(invoke_target, "", is_generic, abi_marshalers, is_noexcept(method))); - })); - } - - void write_interface_members(writer& w, TypeDef const& type) - { - // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. - bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(type); - - auto init_call_variables = [&](writer& w) - { - w.write("\nvar _obj = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(%).TypeHandle);", - bind(type, typedef_name_type::CCW, false)); - }; - - for (auto&& method : type.MethodList()) - { - if (is_special(method)) - { - continue; - } - method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method); - w.write(R"( -unsafe % %%(%) -{% -%%; -} -)", - bind(signature), - bind([&](writer& w) - { - w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); - }), - method.Name(), - bind_list(", ", signature.params()), - bind(init_call_variables), - signature.return_signature() ? "return " : "", - bind(type, method, "_obj") - ); - } - - for (auto&& prop : type.PropertyList()) - { - auto [getter, setter] = get_property_methods(prop); - w.write(R"( -unsafe % %% -{ -)", -write_prop_type(w, prop), -bind([&](writer& w) - { - w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); - }), - prop.Name()); - - if (getter) - { - auto [invoke_target, is_generic] = get_invoke_info(w, getter); - auto signature = method_signature(getter); - auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - w.write(R"(get -{% -return %; -} -)", - bind(init_call_variables), - bind(type, prop, "_obj") - ); - } - if (setter) - { - if (!getter) - { - auto getter_interface = write_type_name_temp(w, - find_property_interface(w, type, prop.Name()).first, - "%", - typedef_name_type::CCW); - auto getter_cast = "((%)(WindowsRuntimeObject)this)"s; - w.write("get{ return " + getter_cast + ".%; }\n", getter_interface, prop.Name()); - } - auto [invoke_target, is_generic] = get_invoke_info(w, setter); - auto signature = method_signature(setter); - auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - marshalers[0].param_name = "value"; - w.write(R"(set -{% -%; -} -)", - bind(init_call_variables), - bind(type, prop, "_obj") - ); - } - w.write("}\n"); - } - - int index = 0; - for (auto&& evt : type.EventList()) - { - auto semantics = get_type_semantics(evt.EventType()); - auto event_source = w.write_temp("Get_%2()", evt.Name()); - w.write(R"( -event % %% -{ -add -{% -%; -} -remove -{% -%; -} -} -)", - - bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), - bind([&](writer& w) - { - w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); - }), - evt.Name(), - bind(init_call_variables), - bind(type, evt, true, "_obj", false), - bind(init_call_variables), - bind(type, evt, false, "_obj", false)); - index++; - } - } - - void write_generic_method_delegate_variable(writer& w, MethodDef const& method, method_signature const& signature, bool is_parameter_variable = false) - { - w.write("%delegate* %%%", - is_parameter_variable ? "" : "internal unsafe volatile static ", - bind_list(", ", signature.params()), - signature.has_params() ? ", " : "", - bind(signature), - is_parameter_variable ? "" : "_", - method.Name(), - is_parameter_variable ? "" : ";"); - } - - void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index = 6, bool skip_events = false) - { - auto init_call_variables = [&](writer& w) - { - w.write(R"( -using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); -void* ThisPtr = thisValue.GetThisPtrUnsafe(); -)"); - }; - - for (auto&& method : iface.MethodList()) - { - if (is_special(method)) - { - continue; - } - - method_signature signature{ method }; - - auto [invoke_target, is_generic] = get_invoke_info(w, method, abi_methods_start_index); - w.write(R"( -[MethodImpl(MethodImplOptions.NoInlining)] -public static unsafe % %(WindowsRuntimeObjectReference thisReference%%) -{%} -)", - bind(signature), - method.Name(), - signature.has_params() ? ", " : "", - bind_list(", ", signature.params()), - bind([&](writer& w) { - init_call_variables(w); - write_abi_method_call(w, signature, invoke_target, "_obj", is_generic, false, is_noexcept(method)); - })); - } - - for (auto&& prop : iface.PropertyList()) - { - auto [getter, setter] = get_property_methods(prop); - - if (getter) - { - auto signature = method_signature(getter); - auto [invoke_target, is_generic] = get_invoke_info(w, getter, abi_methods_start_index); - auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - w.write(R"( -[MethodImpl(MethodImplOptions.NoInlining)] -public static unsafe % %(WindowsRuntimeObjectReference thisReference) -{%} -)", - write_prop_type(w, prop), - prop.Name(), - bind([&](writer& w) { - init_call_variables(w); - write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); - })); - } - if (setter) - { - auto [invoke_target, is_generic] = get_invoke_info(w, setter, abi_methods_start_index); - auto signature = method_signature(setter); - auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - marshalers[0].param_name = "value"; - - w.write(R"( -[MethodImpl(MethodImplOptions.NoInlining)] -public static unsafe void %(WindowsRuntimeObjectReference thisReference, % value) -{%} -)", - prop.Name(), - write_prop_type(w, prop, true), - bind([&](writer& w) { - init_call_variables(w); - write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); - })); - } - w.write("\n"); - } - - if (!skip_events) - { - int index = 0; - for (auto&& evt : iface.EventList()) - { - w.write(R"( -% - -public static % %(object thisObject, WindowsRuntimeObjectReference thisReference) -{ - % - return _%.GetOrAdd( - key: thisObject, - valueFactory: static (_, thisReference) => %, - factoryArgument: thisReference); -} -)", - bind(evt), - bind(get_type_semantics(evt.EventType()), typedef_name_type::EventSource, false), - evt.Name(), - bind(evt), - evt.Name(), - bind(evt, abi_methods_start_index)); - index++; - } - } - } - - struct required_interface - { - std::string members; - }; - - void write_required_interface_members_for_abi_type( - writer& w, - TypeDef const& type, - std::map& required_interfaces) - { - auto write_required_interface = [&](TypeDef const& iface) - { - auto interface_name = write_type_name_temp(w, iface); - if (required_interfaces.find(interface_name) != required_interfaces.end()) - { - // We've already written this required interface, so don't write it again. - return; - } - - if (auto mapping = get_mapped_type(iface.TypeNamespace(), iface.TypeName())) - { - auto remove_enumerable = [&](std::string generic_enumerable = "") - { - required_interfaces["global::System.Collections.IEnumerable"] = {}; - if(generic_enumerable.empty()) return; - required_interfaces[std::move(generic_enumerable)] = {}; - }; - - bool mapping_written = true; - if (mapping->abi_name == "IIterable`1") // IEnumerable`1 - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind(true)) - }; - remove_enumerable(); - } - else if (mapping->abi_name == "IIterator`1") // IEnumerator`1 - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind()), - }; - } - else if (mapping->abi_name == "IMapView`2") // IReadOnlyDictionary`2 - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind(true)), - }; - remove_enumerable(w.write_temp("global::System.Collections.Generic.IEnumerable>", key, value)); - } - else if (mapping->abi_name == "IMap`2") // IDictionary - { - auto key = w.write_temp("%", bind(0)); - auto value = w.write_temp("%", bind(1)); - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind(true)), - }; - remove_enumerable(w.write_temp("global::System.Collections.Generic.IEnumerable>", key, value)); - } - else if (mapping->abi_name == "IVectorView`1") // IReadOnlyList`1 - { - auto element = w.write_temp("%", bind(0)); - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind(true)), - }; - remove_enumerable(w.write_temp("global::System.Collections.Generic.IEnumerable<%>", element)); - } - else if (mapping->abi_name == "IVector`1") // IList`1 - { - auto element = w.write_temp("%", bind(0)); - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind(true)), - }; - remove_enumerable(w.write_temp("global::System.Collections.Generic.IEnumerable<%>", element)); - } - else if (mapping->abi_name == "IBindableIterable") // IEnumerable - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind("((global::System.Collections.IEnumerable)(WindowsRuntimeObject)this)")) - }; - } - else if (mapping->abi_name == "IBindableVector") // IList - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind( - "((global::System.Collections.IList)(WindowsRuntimeObject)this)", - true, - true)) - }; - remove_enumerable(); - } - else if (mapping->mapped_name == "IDisposable") - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind()) - }; - } - else if (mapping->mapped_name == "INotifyDataErrorInfo") - { - required_interfaces[std::move(interface_name)] = - { - w.write_temp("%", bind()) - }; - } - else - { - mapping_written = false; - } - - if (mapping_written) - { - return; - } - } - - auto methods = w.write_temp("%", - [&](writer& w) - { - for (auto&& method : iface.MethodList()) - { - if (!method.SpecialName()) - { - std::string method_target = w.write_temp("((%)(WindowsRuntimeObject)this)", bind(iface, typedef_name_type::Projected, false)); - auto return_type = w.write_temp("%", bind(method_signature{ method })); - write_explicitly_implemented_method_for_abi(w, method, return_type, iface, method_target); - } - } - w.write_each(iface.PropertyList(), iface); - w.write_each(iface.EventList(), iface); - }); - required_interfaces[std::move(interface_name)] = { methods }; - }; - - for (auto&& iface : type.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(iface.Interface()), [&](auto type) - { - if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute") || !is_exclusive_to(type)) - { - write_required_interface(type); - write_required_interface_members_for_abi_type(w, type, required_interfaces); - } - }); - } - } - - void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool include_exclusive_interface, bool includeWindowsRuntimeObject) - { - auto delimiter{ " : " }; - auto write_delimiter = [&]() - { - w.write(delimiter); - delimiter = ", "; - }; - - if (!std::holds_alternative(base_semantics)) - { - write_delimiter(); - write_projection_type(w, base_semantics); - } - else if (includeWindowsRuntimeObject) - { - write_delimiter(); - w.write("WindowsRuntimeObject"); - } - - for (auto&& iface : type.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(iface.Interface()), [&](auto type) - { - if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute") || !is_exclusive_to(type) || include_exclusive_interface) - { - write_delimiter(); - w.write("%", bind(type, typedef_name_type::CCW, false)); - - // If we're emitting a reference assembly, skip declaring the 'IWindowsRuntimeInterface' types. - // These are only needed at runtime to support the interface marshaller, and it shouldn't be part - // of the public API surface of projection assemblies. - if (includeWindowsRuntimeObject && !settings.reference_projection) - { - write_delimiter(); - w.write("IWindowsRuntimeInterface<%>", bind(type, typedef_name_type::CCW, false)); - } - } - }); - } - } - - std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string) - { - method_signature signature{ method }; - if (is_special(method)) - { - bool getter = starts_with(method.Name(), "get_"); - bool setter = starts_with(method.Name(), "put_"); - if (getter || setter) - { - std::string suffix{}; - auto prop_type = getter ? signature.return_signature().Type() : signature.params()[0].second->Type(); - if (prop_type.is_szarray()) - { - return ""; - } - call(get_type_semantics(prop_type), - [&](guid_type) { suffix = "Guid"; }, - [&](type_type) { throw_invalid("System.Type not implemented"); }, - [&](fundamental_type const& type) { suffix = get_delegate_type_suffix(type); }, - [&](generic_type_index const& /*var*/) {}, - [&](type_definition const& /*type*/) {}, - [&](auto) { suffix = "Object"; }); - if (!suffix.empty()) - { - return w.write_temp("%_PropertyAs%", (getter ? "_get" : "_put"), suffix); - } - } - else if (starts_with(method.Name(), "add_")) - { - return "_add_EventHandler"; - } - else if (starts_with(method.Name(), "remove_")) - { - return "_remove_EventHandler"; - } - } - return ""; - } - - - struct generic_abi_param - { - std::string abi_type; - std::string generic_param; - std::string param_name; - }; - - std::pair get_generic_abi_type(writer& w, type_semantics semantics) - { - std::string generic_param{}; - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - auto generic_type_name = w.write_temp("%", bind(index)); - generic_param = generic_type_name + "Abi"; - w.write("Marshaler<%>.AbiType", generic_type_name); - }); - auto generic_abi_type = w.write_temp("%", bind(semantics)); - return {generic_abi_type, generic_param}; - } - - auto get_generic_abi_types(writer& w, method_signature const& signature) - { - std::vector> generic_abi_types; - auto add_generic_abi_type = [&](TypeSig sig, bool byref) - { - auto const [generic_abi_type, generic_param] = get_generic_abi_type(w, get_type_semantics(sig)); - generic_abi_types.push_back({w.write_temp(!generic_param.empty() ? "%%" : "typeof(%)%", - generic_abi_type, byref ? (settings.netstandard_compat ? ".MakeByRefType()" : ".MakePointerType()") : ""), generic_param }); - }; - - auto add_array_param = [&](param_category category) - { - XLANG_ASSERT(category > param_category::out); - switch (category) - { - case param_category::pass_array: - generic_abi_types.push_back({ "typeof(int)", "" }); - generic_abi_types.push_back({ "typeof(IntPtr)", "" }); - break; - case param_category::fill_array: - generic_abi_types.push_back({ "typeof(int)", "" }); - generic_abi_types.push_back({ "typeof(IntPtr).MakeByRefType()", "" }); - break; - case param_category::receive_array: - generic_abi_types.push_back({ "typeof(int).MakeByRefType()", "" }); - generic_abi_types.push_back({ "typeof(IntPtr).MakeByRefType()", "" }); - break; - } - }; - - for (auto&& param : signature.params()) - { - param_category category = get_param_category(param); - if (category <= param_category::out) - { - add_generic_abi_type(param.second->Type(), category == param_category::out); - } - else - { - add_array_param(category); - } - } - if (signature.return_signature()) - { - if (!signature.return_signature().Type().is_szarray()) - { - add_generic_abi_type(signature.return_signature().Type(), true); - } - else - { - add_array_param(param_category::receive_array); - } - } - return generic_abi_types; - } - - void write_abi_signature(writer& w, MethodDef const& method) - { - bool is_generic = distance(method.GenericParam()) > 0; - method_signature signature{ method }; - auto generic_abi_types = get_generic_abi_types(w, signature); - bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - - if (!is_generic && !have_generic_params) - { - w.write("(%)", bind(signature)); - return; - } - w.write("(void* thisPtr"); - int index = 0; - for (auto&& param : signature.params()) - { - auto generic_type = generic_abi_types[index++].second; - auto param_cat = get_param_category(param); - if (!generic_type.empty() && (param_cat <= param_category::out)) - { - std::string_view param_prefix = ""; - std::string_view param_suffix = ""; - - if (param_cat == param_category::ref) - { - param_suffix = "*"; - } - - if (param_cat == param_category::out) - { - param_suffix = "*"; - } - - w.write(", %%% %", - param_prefix, - generic_type, - param_suffix, - bind(param)); - } - else - { - write_abi_parameter(w, param); - } - } - if (auto return_sig = signature.return_signature()) - { - auto generic_type = generic_abi_types[index++].second; - if (!return_sig.Type().is_szarray() && !generic_type.empty()) - { - w.write(", %* %", generic_type, - bind(signature.return_param_name())); - } - else - { - write_abi_return(w, signature); - } - } - w.write(")"); - } - - struct managed_marshaler - { - std::string param_name; - param_category category; - std::string param_type; - std::string local_type; - std::string marshaler_type; - std::string abi_type; - bool abi_boxed; - std::string interop_dll_type; - bool marshal_by_object_reference_value; - bool is_blittable; - bool is_boxed_value; - - bool is_out() const - { - return (category == param_category::out) || - (category == param_category::receive_array); - } - - bool is_ref() const - { - return (category == param_category::fill_array); - } - - bool is_array() const - { - return category >= param_category::pass_array; - } - - bool is_marshal_by_object_reference_value() const - { - return marshal_by_object_reference_value; - } - - std::string get_param_local(writer& w) const - { - return w.write_temp("__%", param_name); - } - - void write_local(writer& w) const - { - if (category == param_category::in || category == param_category::ref) - return; - - if (category == param_category::pass_array || category == param_category::fill_array) - { - if (is_blittable) - { - w.write("%<%> __% = new(%, (int)__%Size);\n", - category == param_category::pass_array ? "ReadOnlySpan" : "Span", - param_type, - param_name, - bind(param_name), - param_name); - } - else - { - w.write(R"( -Unsafe.SkipInit(out InlineArray16<%> __%_inlineArray); -%[] __%_arrayFromPool = null; -Span<%> __% = __%Size <= 16 - ? __%_inlineArray[..(int)__%Size] - : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent((int)__%Size)); -)", - // SkipInit - param_type, - param_name, - // Array - param_type, - param_name, - // Span - param_type, - param_name, - param_name, - param_name, - param_name, - param_name, - param_type, - param_name); - } - return; - } - - std::string_view out_local_type; - if (param_type == "bool") - { - out_local_type = is_array() ? "bool[]" : "bool"; - } - else if (param_type == "char") - { - out_local_type = is_array() ? "char[]" : "char"; - } - else - { - out_local_type = local_type; - } - w.write("% __% = default;\n", - out_local_type, - param_name); - } - - void write_out_initialize(writer& w) const - { - XLANG_ASSERT(is_out()); - w.write("*% = default;\n", bind(param_name)); - if (is_array()) - { - w.write("*__%Size = default;\n", param_name); - } - } - - void write_convert_to_managed_function(writer& w) const - { - if (is_array() || is_out()) - { - return; - } - - if (interop_dll_type != "") - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] -static extern % ConvertToManaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, void* value); - -)", -param_type, -param_name, -interop_dll_type); - } - } - - void write_convert_to_unmanaged_function(writer& w) const - { - if (!is_out()) - { - return; - } - - if (interop_dll_type != "") - { - if (is_array()) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern void ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, ReadOnlySpan<%> span, out uint length, out %* data); - -)", -param_name, -interop_dll_type, -param_type, -abi_type); - } - else - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, % value); - -)", -param_name, -interop_dll_type, -param_type); - } - } - } - - void write_copy_to_managed(writer& w) const - { - if (!is_blittable && category == param_category::pass_array) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] -static extern void CopyToManaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, uint length, %* data, Span<%> span); - -)", - param_name, - interop_dll_type, - abi_type, - param_type); - - w.write("CopyToManaged_%(null, __%Size, (%*)%, __%);", - param_name, - param_name, - abi_type, - bind(param_name), - param_name); - } - } - - void write_marshal_to_managed(writer& w) const - { - if(is_out() || is_ref()) - { - w.write("% __%", is_out() ? "out" : "", param_name); - } - else if (marshaler_type.empty()) - { - std::string_view format_string; - if (category == param_category::ref) - { - format_string = "*%"; - } - else - { - format_string = "%"; - } - w.write(format_string, bind(param_name)); - } - else if (is_array()) - { - w.write("__%", param_name); - } - else if (is_boxed_value) - { - w.write("%.UnboxToManaged(%)", - marshaler_type, - bind(param_name)); - } - else if (interop_dll_type != "") - { - w.write("ConvertToManaged_%(null, %%)", - param_name, - category == param_category::ref ? "*" : "", - bind(param_name)); - } - else - { - w.write("%.ConvertToManaged(%%)", - marshaler_type, - category == param_category::ref ? "*" : "", - bind(param_name)); - } - } - - void write_marshal_from_managed(writer& w) const - { - if (!is_ref() && (!is_out() || local_type.empty())) - return; - auto param_local = get_param_local(w); - if (is_ref()) - { - if (is_blittable) - { - return; - } - - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] -static extern void CopyToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop")] object _, ReadOnlySpan<%> span, uint length, %* data); - -CopyToUnmanaged_%(null, __%, __%Size, (%*)%); - -)", - // CopyToUnmanaged function - param_name, - interop_dll_type, - param_type, - abi_type, - // function call - param_name, - param_name, - param_name, - abi_type, - bind(param_name)); - - return; - } - - if (is_array()) - { - w.write("ConvertToUnmanaged_%(null, __%, out *__%Size, out *%);\n", param_name, param_name, param_name, bind(param_name)); - } - else - { - w.write("*% = ", bind(param_name)); - - if (marshaler_type.empty()) - { - if (local_type == "void*") - { - w.write("%.ConvertToUnmanaged(%);", - param_type, - param_local); - } - else - { - w.write("%;", param_local); - } - } - else if (is_boxed_value) - { - w.write("%.BoxToUnmanaged(%).DetachThisPtrUnsafe();", - marshaler_type, - param_local); - } - else - { - w.write("%%ConvertToUnmanaged%(%%)%;", - abi_boxed ? w.write_temp("(%)", param_type) : "", - interop_dll_type != "" ? "" : marshaler_type + ".", - interop_dll_type != "" ? "_" + param_name : "", - interop_dll_type != "" ? "null, " : "", - param_local, - is_marshal_by_object_reference_value() ? ".DetachThisPtrUnsafe()" : ""); - } - } - - w.write("\n"); - } - - bool need_dispose() const - { - return is_array() && !is_blittable && (category == param_category::pass_array || category == param_category::fill_array); - } - - void write_dispose(writer& w) const - { - if (!need_dispose()) - { - return; - } - - w.write(R"( -if (__%_arrayFromPool is not null) -{ -global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); -} -)", - param_name, - param_type, - param_name); - } - }; - - auto get_managed_marshalers(writer& w, method_signature const& signature, bool /*is_generic*/, bool is_generic_instantiation_class) - { - std::vector marshalers; - concurrency::concurrent_unordered_set generic_instantiations; - - std::function set_marshaler = - [&](writer& w, type_semantics const& semantics, managed_marshaler& m) - { - m.param_type = w.write_temp("%", bind(semantics)); - - auto get_abi_type = [&]() - { - return w.write_temp("%", bind(semantics, typedef_name_type::ABI, true)); - }; - - auto set_typedef_marshaler = [&](TypeDef const& type) - { - switch (get_category(type)) - { - case category::enum_type: - break; - case category::struct_type: - if (!is_type_blittable(type)) - { - if (!m.is_array()) - { - m.marshaler_type = get_abi_type() + "Marshaller"; - } - m.local_type = m.param_type; - } - break; - case category::interface_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - m.local_type = m.param_type; - m.marshal_by_object_reference_value = true; - if (is_projected_as_nullable(type)) - { - m.is_boxed_value = true; - auto generic_arg = w.get_generic_arg(0); - call(generic_arg, - [&](guid_type) - { - m.marshaler_type = "ABI.System.GuidMarshaller"; - }, - [&](type_type) - { - m.marshaler_type = "ABI.System.TypeMarshaller"; - }, - [&](type_definition const& type) { - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - }, - [&](fundamental_type const& type) - { - m.marshaler_type = w.write_temp("ABI.System.%Marshaller", bind(type)); - }, - [&](auto const&) {}); - } - else if (distance(type.GenericParam()) > 0) - { - m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); - } - break; - case category::class_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - m.local_type = m.param_type; - m.marshal_by_object_reference_value = true; - break; - case category::delegate_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); - m.local_type = m.param_type; - m.marshal_by_object_reference_value = true; - if (distance(type.GenericParam()) > 0) - { - m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); - } - break; - } - }; - - call(semantics, - [&](object_type const&) - { - m.marshaler_type = "WindowsRuntimeObjectMarshaller"; - m.local_type = "object"; - m.marshal_by_object_reference_value = true; - }, - [&](type_definition const& type) - { - set_typedef_marshaler(type); - }, - [&](generic_type_index const& var) - { - if (is_generic_instantiation_class) - { - set_marshaler(w, w.get_generic_arg(var.index), m); - } - else - { - m.param_type = get_generic_abi_type(w, semantics).second; - m.local_type = w.write_temp("%", bind(semantics)); - m.marshaler_type = w.write_temp("Marshaler<%>", m.local_type); - m.abi_boxed = true; - } - }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - set_typedef_marshaler(type.generic_type); - }, - [&](fundamental_type type) - { - if (type == fundamental_type::String) - { - m.marshaler_type = "HStringMarshaller"; - m.local_type = m.is_out() ? "string" : ""; - } - }, - [&](auto const&) {}); - - if ((m.is_out() || (m.category == param_category::ref)) && m.local_type.empty()) - { - m.local_type = w.write_temp("%", bind(semantics)); - } - if (m.is_array()) - { - if (m.marshaler_type.empty()) - { - m.is_blittable = is_type_blittable(semantics); - m.marshaler_type = is_type_blittable(semantics) ? "MarshalBlittable" : "MarshalNonBlittable"; - m.marshaler_type += "<" + m.param_type + ">"; - } - m.local_type = (m.local_type.empty() ? m.param_type : m.local_type) + "[]"; - m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); - m.abi_type = w.write_temp("%", bind(semantics)); - } - }; - - for (auto&& param : signature.params()) - { - managed_marshaler m{ - std::string(param.first.Name()) - }; - m.category = get_param_category(param); - set_marshaler(w, get_type_semantics(param.second->Type()), m); - marshalers.push_back(std::move(m)); - } - - if (auto ret = signature.return_signature()) - { - managed_marshaler m{ - std::string(signature.return_param_name()), - ret.Type().is_szarray() ? param_category::receive_array : param_category::out - }; - set_marshaler(w, get_type_semantics(ret.Type()), m); - return std::tuple{ marshalers, m, generic_instantiations }; - } - - return std::tuple{ marshalers, managed_marshaler{}, generic_instantiations }; - } - - void write_managed_method_call(writer& w, method_signature signature, std::string invoke_expression_format, bool is_generic_instantiation_class = false) - { - auto generic_abi_types = get_generic_abi_types(w, signature); - bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - auto managed_marshalers = get_managed_marshalers(w, signature, have_generic_params, is_generic_instantiation_class); - auto marshalers = std::get<0>(managed_marshalers); - auto return_marshaler = std::get<1>(managed_marshalers); - auto generic_instantiations = std::get<2>(managed_marshalers); - auto return_sig = signature.return_signature(); - - w.write( -R"( -% -% -% -try -{ -% -% -%% -return 0; -} -catch (Exception __exception__) -{ -return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); -}% -)", - [&](writer& w) { - w.write(bind_each([](writer& w, managed_marshaler const& m) - { - m.write_convert_to_managed_function(w); - m.write_convert_to_unmanaged_function(w); - }, marshalers)); - - if (return_sig) - { - return_marshaler.write_convert_to_unmanaged_function(w); - } - }, - [&](writer& w) { - if (!return_sig) return; - return_marshaler.write_local(w); - }, - [&](writer& w) { - w.write(bind_each([](writer& w, managed_marshaler const& m) - { - if (m.is_out()) - { - m.write_out_initialize(w); - } - }, marshalers)); - if (return_sig) - { - return_marshaler.write_out_initialize(w); - } - w.write(bind_each([](writer& w, managed_marshaler const& m) - { - m.write_local(w); - }, marshalers)); - }, - [&](writer& w) - { - w.write(bind_each([](writer& w, managed_marshaler const& m) - { - m.write_copy_to_managed(w); - }, marshalers)); - }, - [&](writer& w) - { - if (return_sig) - { - w.write("__% = ", return_marshaler.param_name); - } - - w.write(R"(%;)", - bind([&](writer& w) - { - w.write(invoke_expression_format, - bind_list([](writer& w, managed_marshaler const& m) - { - m.write_marshal_to_managed(w); - }, ",\n ", marshalers)); - })); - }, - bind_each([](writer& w, managed_marshaler const& m) - { - m.write_marshal_from_managed(w); - }, marshalers), - [&](writer& w) { - if (!return_sig) return; - return_marshaler.write_marshal_from_managed(w); - }, - [&](writer& w) { - bool write_dispose = false; - w.write(bind_each([&](writer&, managed_marshaler const& m) - { - write_dispose |= m.need_dispose(); - }, marshalers)); - - if (write_dispose) - { - w.write(R"( -finally -{ -% -} -)", - bind_each([](writer& w, managed_marshaler const& m) - { - m.write_dispose(w); - }, marshalers) -); - } - }); - } - - void write_method_abi_invoke(writer& w, MethodDef const& method) - { - if (method.SpecialName()) return; - - method_signature signature{ method }; - auto return_sig = signature.return_signature(); - auto type_name = write_type_name_temp(w, method.Parent()); - auto vmethod_name = get_vmethod_name(w, method.Parent(), method); - - auto generic_abi_types = get_generic_abi_types(w, signature); - - w.write(R"( -[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvMemberFunction) })] -private static unsafe int Do_Abi_%% -{ -% -})", -vmethod_name, -bind(method), -bind( - signature, - w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).%%", - type_name, - method.Name(), - "(%)"), - false)); - } - - void write_property_abi_invoke(writer& w, Property const& prop) - { - auto [getter, setter] = get_property_methods(prop); - auto type_name = write_type_name_temp(w, prop.Parent()); - if (setter) - { - method_signature setter_sig{ setter }; - auto vmethod_name = get_vmethod_name(w, setter.Parent(), setter); - - // WinRT properties can't be indexers. - XLANG_ASSERT(setter_sig.params().size() == 1); - - w.write( - R"( -[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvMemberFunction) })] -private static unsafe int Do_Abi_%% -{ -% -})", - vmethod_name, - bind(setter), - bind( - setter_sig, - w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).% = %%", - type_name, - prop.Name(), - "%", - prop.Type().Type().is_szarray() ? ".ToArray()" : ""), - false)); - } - - if (getter) - { - method_signature getter_sig{ getter }; - auto vmethod_name = get_vmethod_name(w, getter.Parent(), getter); - - // WinRT properties can't be indexers. - XLANG_ASSERT(getter_sig.params().size() == 0); - w.write( - R"( -[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvMemberFunction) })] -private static unsafe int Do_Abi_%% -{ -% -})", - vmethod_name, - bind(getter), - bind( - getter_sig, - w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).%%", - type_name, - prop.Name(), - "%"), - false)); - } - } - - void write_event_abi_invoke(writer& w, Event const& evt) - { - auto type_name = write_type_name_temp(w, evt.Parent()); - auto semantics = get_type_semantics(evt.EventType()); - auto generic_event_type = std::holds_alternative(semantics); - auto [add_method, remove_method] = get_event_methods(evt); - auto add_signature = method_signature{ add_method }; - - auto handler_parameter_name = add_signature.params().back().first.Name(); - auto add_handler_event_token_name = add_signature.return_param_name(); - auto remove_handler_event_token_name = method_signature{ remove_method }.params().back().first.Name(); - - w.write(R"( -private static ConditionalWeakTable<%, EventRegistrationTokenTable<%>> _% -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - [MethodImpl(MethodImplOptions.NoInlining)] - static ConditionalWeakTable<%, EventRegistrationTokenTable<%>> MakeTable() - { - _ = global::System.Threading.Interlocked.CompareExchange(ref field, [], null); - - return global::System.Threading.Volatile.Read(in field); - } - - return global::System.Threading.Volatile.Read(in field) ?? MakeTable(); - } -} -)", - type_name, - bind(semantics, typedef_name_type::Projected, false), - evt.Name(), - type_name, - bind(semantics, typedef_name_type::Projected, false)); - - w.write( - R"( -[UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] -private static unsafe int Do_Abi_%% -{ -*% = default; -try -{ -var __this = ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr); -% -*% = _%.GetOrCreateValue(__this).AddEventHandler(__handler); -__this.% += __handler; -return 0; -} -catch (Exception __exception__) -{ -return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); -} -})", - get_vmethod_name(w, add_method.Parent(), add_method), - bind(add_method), - add_handler_event_token_name, - type_name, - [&](writer& w) { - if (generic_event_type) - { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] -static extern % ConvertToManaged([UnsafeAccessorType("%, WinRT.Interop")] object _, void* value); - -var __handler = ConvertToManaged(null, %); -)", - bind(semantics, typedef_name_type::Projected, false), - bind(semantics, typedef_name_type::Marshaller), - handler_parameter_name -); - } - else - { - w.write("var __handler = %Marshaller.ConvertToManaged(%);", - bind(semantics, typedef_name_type::ABI, false), - handler_parameter_name); - } - }, - add_handler_event_token_name, - evt.Name(), - evt.Name()); - w.write( - R"( -[UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] -private static unsafe int Do_Abi_%% -{ -try -{ -var __this = ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr); -if(__this is not null && _%.TryGetValue(__this, out var __table) && __table.RemoveEventHandler(%, out var __handler)) -{ -__this.% -= __handler; -} -return 0; -} -catch (Exception __exception__) -{ -return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); -} -})", - get_vmethod_name(w, remove_method.Parent(), remove_method), - bind(remove_method), - type_name, - evt.Name(), - remove_handler_event_token_name, - evt.Name()); - } - - void get_vtable_members( - writer& w, - TypeDef const& type, - std::vector& nongeneric_delegates, - std::vector& method_marshals_to_abi, - std::vector& method_marshals_to_projection, - std::vector& vtable_delegates, - std::vector& method_create_function_pointers_to_projection, - std::vector& type_declarations, - std::vector& vtable_members) - { - auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); - auto methods = type.MethodList(); - auto is_generic = distance(type.GenericParam()) > 0; - for (auto& method : methods) - { - bool signature_has_generic_parameters{}; - - auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - bool have_generic_type_parameters = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - - auto vmethod_name = get_vmethod_name(w, type, method); - auto delegate_type = get_vmethod_delegate_type(w, method, vmethod_name); - std::string vtable_field_type; - bool function_pointer = false; - if (vtable_field_type == "") - { - delegate_type = nongenerics_class + "." + vmethod_name; - writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { - signature_has_generic_parameters = true; - }); - - auto delegate_definition = w.write_temp("public unsafe delegate int %(%);\n", - vmethod_name, - bind(method_signature{ method })); - - if (signature_has_generic_parameters) - { - delegate_type = vtable_field_type = "global::System.Delegate"; - } - else - { - if (settings.netstandard_compat || is_generic) - { - nongeneric_delegates.push_back(delegate_definition); - } - - vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); - function_pointer = true; - } - } - else - { - // We're a well-known delegate type, but we still need to get the function pointer type. - vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); - function_pointer = true; - } - if (!function_pointer) - { - vtable_members.emplace_back(w.write_temp("public % %;", vtable_field_type, vmethod_name)); - } - else if (settings.netstandard_compat || is_generic) - { - // Work around https://github.com/dotnet/runtime/issues/37295 - vtable_members.emplace_back(w.write_temp(R"( -private void* _%; -public % % { get => (%)_%; set => _%=(void*)value; } -)", - vmethod_name, vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name)); - } - else - { - // Work around C# compiler's lack of support for UnmanagedCallersOnly - vtable_members.emplace_back(w.write_temp(R"( -private delegate* unmanaged[Stdcall]<%, int> _%; -public % % { get => (%)_%; set => _%=(delegate* unmanaged[Stdcall]<%, int>)value; } -)", - bind(method_signature{ method }), vmethod_name, - vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name, - bind(method_signature{ method }))); - } - uint32_t const delegate_cache_index = method.index() - methods.first.index(); - uint32_t const vtable_index = delegate_cache_index + 6; - if (is_generic) - { - method_marshals_to_abi.emplace_back(signature_has_generic_parameters ? - w.write_temp("% = Marshal.GetDelegateForFunctionPointer(vftbl[%], %_Type);\n", - vmethod_name, vtable_index, vmethod_name) : - w.write_temp("% = (%)(vftbl[%]);\n", - vmethod_name, vtable_field_type, vtable_index) - ); - - method_marshals_to_projection.emplace_back(signature_has_generic_parameters ? - w.write_temp("nativeVftbl[%] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.%);\n", - vtable_index, vmethod_name) : - w.write_temp("nativeVftbl[%] = (IntPtr)AbiToProjectionVftable._%;", vtable_index, vmethod_name) - ); - - if (have_generic_type_parameters) - { - auto create_delegate = - w.write_temp(R"(global::System.Delegate.CreateDelegate(%, typeof(%).GetMethod("Do_Abi_%", BindingFlags.NonPublic | BindingFlags.Static)))", - !signature_has_generic_parameters ? w.write_temp("typeof(%)", vtable_field_type) : vmethod_name + "_Type", - bind(type), - vmethod_name); - vtable_delegates.emplace_back(create_delegate); - - method_create_function_pointers_to_projection.emplace_back( - w.write_temp(R"(% = %%)", - vmethod_name, - !signature_has_generic_parameters ? w.write_temp("(%)", vtable_field_type) : "", - create_delegate)); - - if (signature_has_generic_parameters) - { - type_declarations.emplace_back(w.write_temp("Type %_Type = %(new Type[]{ typeof(void*), %typeof(int) });\n", - vmethod_name, - settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", - bind_each([&](writer& w, auto&& pair) - { - w.write("%, ", pair.first); - }, generic_abi_types))); - } - } - else - { - auto create_delegate = w.write_temp("new %(Do_Abi_%)", - delegate_type, - vmethod_name); - vtable_delegates.emplace_back(create_delegate); - method_create_function_pointers_to_projection.emplace_back( - w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = %)", - vmethod_name, - delegate_cache_index, - create_delegate)); - } - } - else if (settings.netstandard_compat) - { - auto create_delegate = w.write_temp("new %(Do_Abi_%)", - delegate_type, - vmethod_name); - vtable_delegates.emplace_back(create_delegate); - method_create_function_pointers_to_projection.emplace_back( - w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = %)", - vmethod_name, - delegate_cache_index, - create_delegate)); - } - else - { - // Work around C# compiler's lack of support for UnmanagedCallersOnly - method_create_function_pointers_to_projection.emplace_back( - w.write_temp("_% = &Do_Abi_%", - vmethod_name, vmethod_name) - ); - } - } - } - - void write_vtable(writer& w, TypeDef const& type, std::string const& type_name, - std::string const& nongenerics_class, - std::vector& nongeneric_delegates) - { - auto methods = type.MethodList(); - auto is_generic = distance(type.GenericParam()) > 0; - std::vector method_marshals_to_abi; - std::vector method_marshals_to_projection; - std::vector method_create_delegates_to_projection; - - w.write(R"(% -public struct Vftbl -{ -internal IInspectable.Vftbl IInspectableVftbl; -%%%%%% -})", - bind(type), - bind_each([&](writer& w, MethodDef const& method) - { - bool signature_has_generic_parameters{}; - - auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - bool have_generic_type_parameters = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - - auto vmethod_name = get_vmethod_name(w, type, method); - auto delegate_type = get_vmethod_delegate_type(w, method, vmethod_name); - std::string vtable_field_type; - bool function_pointer = false; - if(vtable_field_type == "") - { - delegate_type = nongenerics_class + "." + vmethod_name; - writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { - signature_has_generic_parameters = true; - }); - auto delegate_definition = w.write_temp("public unsafe delegate int %(%);\n", - vmethod_name, - bind(method_signature{ method })); - if (signature_has_generic_parameters) - { - delegate_type = vtable_field_type = "global::System.Delegate"; - } - else - { - if (settings.netstandard_compat || is_generic) - { - nongeneric_delegates.push_back(delegate_definition); - } - - vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); - function_pointer = true; - } - } - else - { - // We're a well-known delegate type, but we still need to get the function pointer type. - vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); - function_pointer = true; - } - if (!function_pointer) - { - w.write("public % %;", vtable_field_type, vmethod_name); - } - else if (settings.netstandard_compat || is_generic) - { - // Work around https://github.com/dotnet/runtime/issues/37295 - w.write("private void* _%;\n", vmethod_name); - w.write("public % % { get => (%)_%; set => _%=(void*)value; }\n", - vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name); - } - else - { - // Work around C# compiler's lack of support for UnmanagedCallersOnly - w.write("private delegate* unmanaged[Stdcall]<%, int> _%;\n", bind(method_signature{ method }), vmethod_name); - w.write("public % % { get => (%)_%; set => _%=(delegate* unmanaged[Stdcall]<%, int>)value; }\n", - vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name, - bind(method_signature{ method })); - } - uint32_t const delegate_cache_index = method.index() - methods.first.index(); - uint32_t const vtable_index = delegate_cache_index + 6; - if (is_generic) - { - method_marshals_to_abi.emplace_back(signature_has_generic_parameters ? - w.write_temp("% = Marshal.GetDelegateForFunctionPointer(vftbl[%], %_Type);\n", - vmethod_name, vtable_index, vmethod_name) : - w.write_temp("% = (%)(vftbl[%]);\n", - vmethod_name, vtable_field_type, vtable_index) - ); - - method_marshals_to_projection.emplace_back(signature_has_generic_parameters ? - w.write_temp("nativeVftbl[%] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.%);\n", - vtable_index, vmethod_name) : - w.write_temp("nativeVftbl[%] = (IntPtr)AbiToProjectionVftable._%;", vtable_index, vmethod_name) - ); - - if (have_generic_type_parameters) - { - method_create_delegates_to_projection.emplace_back( - w.write_temp(R"(% = %global::System.Delegate.CreateDelegate(%, typeof(Vftbl).GetMethod("Do_Abi_%", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(%)))", - vmethod_name, - !signature_has_generic_parameters ? w.write_temp("(%)", vtable_field_type) : "", - !signature_has_generic_parameters ? w.write_temp("typeof(%)", vtable_field_type) : vmethod_name + "_Type", - vmethod_name, - bind([&](writer& w, method_signature const& sig) - { - separator s{ w }; - auto write_abi_type = [&](writer& w, type_semantics type) - { - auto const [generic_abi_type, generic_type_parameter] = get_generic_abi_type(w, type); - if (!generic_type_parameter.empty()) - { - s(); - w.write(generic_abi_type); - } - }; - for (auto&& param : sig.params()) - { - write_abi_type(w, get_type_semantics(param.second->Type())); - } - if (sig.return_signature()) - { - write_abi_type(w, get_type_semantics(sig.return_signature().Type())); - } - }, method_signature{ method }))); - } - else - { - method_create_delegates_to_projection.emplace_back( - w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = new %(Do_Abi_%))", - vmethod_name, - delegate_cache_index, - delegate_type, - vmethod_name)); - } - } - else if (settings.netstandard_compat) - { - method_create_delegates_to_projection.emplace_back( - w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = new %(Do_Abi_%))", - vmethod_name, - delegate_cache_index, - delegate_type, - vmethod_name)); - } - else - { - // Work around C# compiler's lack of support for UnmanagedCallersOnly - method_create_delegates_to_projection.emplace_back( - w.write_temp("_% = &Do_Abi_%", - vmethod_name, vmethod_name) - ); - } - }, methods), - [&](writer& w) - { - if (!is_generic) return; - w.write("public static readonly Guid PIID = GuidGenerator.CreateIID(typeof(%));\n", type_name); - w.write(R"(% -internal unsafe Vftbl(IntPtr thisPtr) : this() -{ -var vftblPtr = *(void***)thisPtr; -var vftbl = (IntPtr*)vftblPtr; -IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; -%} -)", - bind_each([&](writer& w, MethodDef const& method) - { - auto vmethod_name = get_vmethod_name(w, type, method); - - if (abi_signature_has_generic_parameters(w, method_signature{ method })) - { - auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - - w.write("public static readonly Type %_Type = %(new Type[]{ typeof(void*), %typeof(int) });\n", - vmethod_name, - settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", - bind_each([&](writer& w, auto&& pair) - { - w.write("%, ", pair.first); - }, generic_abi_types)); - } - }, methods), - bind_each(method_marshals_to_abi) - ); - }, - bind([&](writer& w) - { - if (is_generic) - { - w.write(R"( -private static readonly Vftbl AbiToProjectionVftable; -public static readonly IntPtr AbiToProjectionVftablePtr; -private static Delegate[] DelegateCache = new Delegate[%]; -static unsafe Vftbl() -{ -AbiToProjectionVftable = new Vftbl -{ -IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -% -}; -var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * %); -% -AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; -} -)", - std::to_string(distance(methods)), - bind_list(",\n", method_create_delegates_to_projection), - std::to_string(distance(methods)), - bind([&](writer& w) - { - if (!is_generic) - { - w.write("Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false);"); - } - else - { - w.write("Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false);\n"); - w.write("%", bind_each(method_marshals_to_projection)); - } - })); - } - else - { - w.write(R"( -public static readonly IntPtr AbiToProjectionVftablePtr; -% -static unsafe Vftbl() -{ -AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * %); -(*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl -{ -IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -% -}; -} -)", - bind([&](writer& w) - { - if (settings.netstandard_compat) - { - w.write("private static Delegate[] DelegateCache = new Delegate[%];", std::to_string(distance(methods))); - } - }), - std::to_string(distance(methods)), - bind_list(",\n", method_create_delegates_to_projection) - ); - } - }), - bind_each(methods), - bind_each(type.PropertyList()), - bind_each(type.EventList()) - ); - } - - void write_authoring_metadata_type(writer& w, TypeDef const& type) - { - if (get_category(type) != category::delegate_type && - get_category(type) != category::class_type) - { - write_winrt_reference_type_attribute(w, type); - } - - if (get_category(type) != category::struct_type && - get_category(type) != category::class_type) - { - write_comwrapper_marshaller_attribute(w, type); - } - - if (get_category(type) != category::class_type) - { - write_value_type_winrt_classname_attribute(w, type); - } - - w.write("%%file static class % {}\n", - bind(type), - bind(type), - bind(type, typedef_name_type::ABI, false)); - } - - void write_contract(writer& w, TypeDef const& type) - { - if (settings.component) - { - return; - } - - auto type_name = write_type_name_temp(w, type); - w.write(R"(%% enum % -{ -} -)", - bind(type, false), - internal_accessibility(), - type_name); - } - - void write_attribute(writer& w, TypeDef const& type) - { - auto type_name = write_type_name_temp(w, type); - - w.write(R"(%%% sealed class %: Attribute -{ -%} -)", - bind(type), - bind(type, true), - internal_accessibility(), - type_name, - [&](writer& w) - { - auto methods = type.MethodList(); - for (auto&& method : methods) - { - if (method.Name() != ".ctor") continue; - method_signature signature{ method }; - w.write("public %(%){}\n", - type_name, - bind_list(", ", signature.params())); - } - for (auto&& field : type.FieldList()) - { - w.write("public % %;\n", - bind(get_type_semantics(field.Signature().Type())), - field.Name()); - } - }); - } - - bool is_default_or_overridable_interface_typedef(writer& w, TypeDef const& iface) - { - if (!is_exclusive_to(iface)) - { - return false; - } - - auto class_type = get_exclusive_to_type(iface); - for (auto&& ii : class_type.InterfaceImpl()) - { - if (!is_default_interface(ii) && !is_overridable(ii)) - { - continue; - } - - bool interface_matches = false; - for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& impl_iface) - { - if (impl_iface == iface) - { - interface_matches = true; - } - }); - - if (interface_matches) - { - return true; - } - } - - return false; - } - - void write_interface(writer& w, TypeDef const& type) - { - XLANG_ASSERT(get_category(type) == category::interface_type); - - // Exclusive interfaces other than for the default and overridable one are not used - // in the projection, so we can skip them unless public_exclusiveto is set. - // We still need to emit them in the reference projection so the merged projection - // generator can discover them and include them as part of the includes to allow the - // ABI types to still get generated. - if (!settings.reference_projection && - !settings.component && - is_exclusive_to(type) && - !settings.public_exclusiveto && - !is_default_or_overridable_interface_typedef(w, type)) - { - return; - } - - // For component interfaces, if it isn't exclusive, we can just use the authored one. - if (settings.component && !is_exclusive_to(type)) - { - return; - } - - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); - - w.write(R"( -%% -%% interface %% -{% -} -)", - // Interface - bind(type), - bind(type), - bind(type, false), - (is_exclusive_to(type) && !settings.public_exclusiveto) || is_projection_internal(type) ? "internal" : "public", - type_name, - bind(type, object_type{}, false, false), - bind(type) - ); - } - - void write_generic_interface_impl_class(writer& w, TypeDef const& iface) - { - w.write(R"( -internal sealed class @Impl% : %, IWinRTObject -{ -private IObjectReference _inner; - -internal @Impl(IObjectReference _inner) -{ -this._inner = _inner; -} - -public static @Impl% CreateRcw(IInspectable obj) => new(obj.ObjRef); - -% - -IObjectReference IWinRTObject.NativeObject => _inner; - -bool IWinRTObject.HasUnwrappableNativeObject => true; - -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() -{ -global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); -return _queryInterfaceCache; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() -{ -global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); -return _additionalTypeData; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - -% -} -)", - iface.TypeName(), - bind(iface), - bind(iface, typedef_name_type::Projected, false), - iface.TypeName(), - iface.TypeName(), - bind(iface), - [&](writer& w) - { - bool hasDerivedGenericInterfaces = distance(iface.GenericParam()) == 0 && has_derived_generic_interface(iface); - std::set writtenInterfaces; - std::function write_objref_defintion = [&](writer& w, type_semantics const& ifaceTypeSemantics) - { - auto objrefname = w.write_temp("%", bind(ifaceTypeSemantics)); - - // When writing derived interfaces of interfaces, we can sometimes encounter duplicate interfaces. - // To prevent writing them multiple times, we catch them here. - if (writtenInterfaces.find(objrefname) != writtenInterfaces.end()) - { - return; - } - writtenInterfaces.insert(objrefname); - - w.write(R"( -private volatile IObjectReference __%; -private IObjectReference Make__%() -{ -% -global::System.Threading.Interlocked.CompareExchange(ref __%, ((IWinRTObject)this).NativeObject.As(%.IID), null); -return __%; -} -private IObjectReference % => __% ?? Make__%(); -)", - objrefname, - objrefname, - [&](writer& w) { - // We initialize the generic interface instantiation class if the respective interface for this - // impl class is not generic but has derived generic interfaces. This is because in those cases - // we know the specific generic instantiations and can initialize them here rather than earlier. - // By deferring it to here, we are able to trim friendly allowing to trim unused interfaces. - if (hasDerivedGenericInterfaces) - { - write_ensure_generic_type_initialized(w, ifaceTypeSemantics); - } - }, - objrefname, - bind(ifaceTypeSemantics, typedef_name_type::StaticAbiClass, false), - objrefname, - objrefname, - objrefname, - objrefname); - - for_typedef(w, ifaceTypeSemantics, [&](auto type) - { - for (auto&& ii : type.InterfaceImpl()) - { - write_objref_defintion(w, get_type_semantics(ii.Interface())); - } - }); - }; - - write_objref_defintion(w, iface); - }, - bind(iface, true)); - } - - void write_static_abi_classes(writer& w, TypeDef const& iface) - { - auto fast_abi_class_val = get_fast_abi_class_for_interface(iface); - if (fast_abi_class_val.has_value()) - { - if (fast_abi_class_val.value().contains_other_interface(iface)) - { - return; - } - } - - // Skip generating event members for exclusive interfaces whose events are now - // inlined in the RCW class. This is safe because the interface and its Methods - // type are internal, so no other type can reference these event methods. - // Only do this for instance interfaces (listed in InterfaceImpl on the class), - // not for statics interfaces (referenced via StaticAttribute), whose events - // are never inlined and still go through the Methods type. - bool skip_exclusive_events = false; - if (is_exclusive_to(iface) && !settings.public_exclusiveto) - { - auto class_type = get_exclusive_to_type(iface); - for (auto&& ii : class_type.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& impl_iface) - { - if (interfaces_equal(impl_iface, iface)) - { - skip_exclusive_events = true; - } - }); - } - } - - auto members = w.write_temp("%", [&](writer& w) { - if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { - write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT, skip_exclusive_events); - return; - } - auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; - // Skip events for the default interface (its events are inlined in the RCW class) - write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index, skip_exclusive_events); - abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); - for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) - { - // Keep events for other interfaces (they are NOT inlined, they still use the old CWT path) - write_static_abi_class_members(w, other_iface, abi_methods_start_index); - abi_methods_start_index += distance(other_iface.MethodList()); - } - }); - - // If all members were skipped (e.g., an exclusive interface with only events), - // omit generating the empty Methods type entirely. - if (members.empty()) - { - return; - } - - w.write(R"( -% static class % -{ -% -} -)", - (is_exclusive_to(iface) && !settings.public_exclusiveto) || is_projection_internal(iface) ? "internal" : "public", - bind(iface, typedef_name_type::StaticAbiClass, false), - members); - } - - // Determines whether to emit the impl type with the vtable. - // For exclusive interfaces which aren't overridable interfaces that are implemented by unsealed types, - // we do not need any of the Do_Abi functions or the vtable logic as we will not create CCWs for them. - // The only exception to this is if public_exclusiveto is set or if it is a component type. - bool emit_impl_type(writer& w, TypeDef const& type) - { - if (settings.component) - { - return true; - } - - if (is_exclusive_to(type) && !settings.public_exclusiveto) - { - bool hasOverridableAttribute = false; - auto exclusive_to_type = get_exclusive_to_type(type); - for (auto&& iface : exclusive_to_type.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(iface.Interface()), [&](auto interface_type) - { - if (type == interface_type && is_overridable(iface)) - { - hasOverridableAttribute = true; - } - }); - - if (hasOverridableAttribute) - { - break; - } - } - - if (!hasOverridableAttribute) - { - return false; - } - } - - return true; - } - - void write_interface_vftbl(writer& w, TypeDef const& type) - { - if (!emit_impl_type(w, type)) - { - return; - } - - w.write(R"( -[StructLayout(LayoutKind.Sequential)] -internal unsafe struct %Vftbl -{ -public delegate* unmanaged[MemberFunction] QueryInterface; -public delegate* unmanaged[MemberFunction] AddRef; -public delegate* unmanaged[MemberFunction] Release; -public delegate* unmanaged[MemberFunction] GetIids; -public delegate* unmanaged[MemberFunction] GetRuntimeClassName; -public delegate* unmanaged[MemberFunction] GetTrustLevel; -% -} -)", - type.TypeName(), - bind_each([&](writer& w, MethodDef const& method) - { - auto vmethod_name = get_vmethod_name(w, type, method); - w.write("public delegate* unmanaged[MemberFunction]<%, int> %;\n", - bind(method_signature{ method }), - vmethod_name); - }, type.MethodList())); - } - - void write_interface_impl(writer& w, TypeDef const& type) - { - if (!emit_impl_type(w, type)) - { - return; - } - - w.write(R"( -public static unsafe class %Impl -{ -[FixedAddressValueType] -private static readonly %Vftbl Vftbl; - -static %Impl() -{ - *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; - % -} - -public static ref readonly Guid IID -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref %; -} - -public static nint Vtable -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)Unsafe.AsPointer(in Vftbl); -} - -% -% -% -} -)", - type.TypeName(), - type.TypeName(), - // static ctor - type.TypeName(), - bind_each([&](writer& w, MethodDef const& method) - { - auto vmethod_name = get_vmethod_name(w, type, method); - w.write("Vftbl.% = &Do_Abi_%;\n", - vmethod_name, - vmethod_name); - }, type.MethodList()), - bind(type), - // Vtable functions - bind_each(type.MethodList()), - bind_each(type.PropertyList()), - bind_each(type.EventList()) -); - } - - void write_interface_idic_impl(writer& w, TypeDef const& type) - { - if (is_exclusive_to(type) && !settings.idic_exclusiveto) - { - return; - } - - std::map required_interfaces; - write_required_interface_members_for_abi_type(w, type, required_interfaces); - - w.write(R"( -[DynamicInterfaceCastableImplementation] -% -file interface % : % -{ -% -% -} -)", - bind(type), - type.TypeName(), - bind(type, typedef_name_type::Projected, false), - bind(type), - [&](writer& w) { - if (!is_exclusive_to(type) || settings.idic_exclusiveto) - { - for (auto required_interface : required_interfaces) - { - w.write("%", required_interface.second.members); - } - } - }); - } - - void write_interface_marshaller(writer& w, TypeDef const& type) - { - if (is_exclusive_to(type)) - { - return; - } - - auto projected_type = w.write_temp("%", bind(type, typedef_name_type::Projected, false)); - w.write(R"( -#nullable enable -public static unsafe class %Marshaller -{ - public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(% value) - { - return WindowsRuntimeInterfaceMarshaller<%>.ConvertToUnmanaged(value, %); - } - - public static %? ConvertToManaged(void* value) - { - return (%?) WindowsRuntimeObjectMarshaller.ConvertToManaged(value); - } -} -#nullable disable -)", - type.TypeName(), - projected_type, - projected_type, - bind(type), - projected_type, - projected_type -); - } - - void write_abi_interface(writer& w, TypeDef const& type) - { - XLANG_ASSERT(get_category(type) == category::interface_type); - auto is_generic = distance(type.GenericParam()) > 0; - if (is_generic) - { - return; - } - - write_static_abi_classes(w, type); - - // Internal projections just need the static ABI methods class. - if (is_projection_internal(type)) - { - return; - }; - - write_interface_vftbl(w, type); - write_interface_impl(w, type); - write_interface_idic_impl(w, type); - write_interface_marshaller(w, type); - } - - void write_custom_query_interface_impl(writer& w, TypeDef const& type) - { - if (settings.reference_projection) - { - return; - } - - bool has_base_class = !std::holds_alternative(get_type_semantics(type.Extends())); - separator s{ w, " || " }; - w.write(R"( -protected override bool IsOverridableInterface(in Guid iid) => %%; -)", - bind_each([&](writer& w, InterfaceImpl const& iface) - { - if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute")) - { - s(); - w.write("% == iid", - bind(get_type_semantics(iface.Interface()))); - } - }, type.InterfaceImpl()), - bind([&](writer& w) - { - if (has_base_class) - { - s(); - w.write("base.IsOverridableInterface(in iid)"); - } - if (s.first) - { - w.write("false"); - } - })); - } - - void write_component_class_marshaller(writer& w, TypeDef const& type) - { - auto projected_type = w.write_temp("%", bind(type, typedef_name_type::Projected, false)); - auto default_type_semantics = get_type_semantics(get_default_interface(type)); - - w.write(R"( -public static unsafe class %Marshaller -{ - public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(% value) - {% - return WindowsRuntimeInterfaceMarshaller<%>.ConvertToUnmanaged(value, %); - } - - public static %? ConvertToManaged(void* value) - { - return (%?) WindowsRuntimeObjectMarshaller.ConvertToManaged(value); - } -} -)", -type.TypeName(), -projected_type, -bind([&](writer& w) { - for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) - { - if (size(interface_type.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, interface_type, true); - } - }); -}), -projected_type, -bind(default_type_semantics), -projected_type, -projected_type -); - } - - void write_wrapper_class(writer& w, TypeDef const& type) - { - if (is_static(type)) - { - return; - } - - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); - auto wrapped_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); - auto default_interface_name = get_default_interface_name(w, type, false, true); - - // This type can be empty, as it is only used for metadata lookup, but not as implementation. - // On modern .NET, we use [WinRTExposedType] to get all implemented interfaces for the vtable. - w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(typeof(%))] -%internal % partial class % -{ -public static % FromAbi(IntPtr thisPtr) -{ -if (thisPtr == IntPtr.Zero) return null; -return MarshalInspectable<%>.FromAbi(thisPtr); -} -} -)", - bind(type), - bind(type), - default_interface_name, - bind(type, false), - bind(type), - type_name, - wrapped_type_name, - wrapped_type_name); - } - - void write_class(writer& w, TypeDef const& type) - { - writer::write_platform_guard guard{ w }; - - if (settings.component) - { - return; - } - - if (is_static(type)) - { - write_static_class(w, type); - return; - } - - auto type_name = write_type_name_temp(w, type); - auto base_semantics = get_type_semantics(type.Extends()); - auto gc_pressure_amount = get_gc_pressure_amount(type); - - w.write(R"( -%%%% %class %% -{ -% -% - -% - -% -% -% -% -% -} -)", - bind(type), - bind(type, true), - bind(type), - (settings.internal) ? "internal" : "public", - bind(type), - // class name - type_name, - bind(type, base_semantics, false, true), - // start of class - bind(type, type.Flags().Sealed()), - // event source properties (lazy-loaded, inline cached) - bind(type), - // ObjectReference constructor - [&](writer& w) - { - if (settings.reference_projection) - { - // If no constructors will be emitted by write_attributed_types, we need to emit - // a private constructor to prevent the C# compiler from auto-generating a public - // default constructor. Activatable factories always emit constructors. Composable - // factories only emit constructors when the factory interface has methods (some - // composable factory interfaces are empty, with composition handled by the base class). - bool has_constructors = false; - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if (factory.activatable || - (factory.composable && factory.type && size(factory.type.MethodList()) > 0)) - { - has_constructors = true; - break; - } - } - if (!has_constructors) - { - w.write("private %() { throw null; }\n", type_name); - } - return; - } - - w.write(R"( -% %(WindowsRuntimeObjectReference nativeObjectReference) -: base(nativeObjectReference) -{ -%%} -)", - type.Flags().Sealed() ? "internal" : "protected internal", - type_name, - [&](writer& w) - { - if (!type.Flags().Sealed()) - { - w.write(R"( -if (GetType() == typeof(%)) -{ -% = NativeObjectReference; -})", - type.TypeName(), - bind(get_type_semantics(get_default_interface(type)))); - } - }, - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); - }); - }, - // Other constructors - bind(type), - // Conditional finalizer - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write(R"(~%() -{ -GC.RemoveMemoryPressure(%); -} -)", - type_name, - gc_pressure_amount); - }, - [&](writer& w) - { - if (settings.reference_projection) - { - return; - } - else if (!type.Flags().Sealed()) - { - w.write(R"( -protected override bool HasUnwrappableNativeObjectReference => GetType() == typeof(%);)", - type.TypeName()); - } - else - { - w.write(R"( -protected override bool HasUnwrappableNativeObjectReference => true;)"); - } - }, - bind(type), - bind(type, false)); - - // TODO: Fast ABI - } - - void write_class_marshaller(writer& w, TypeDef const& type) - { - auto projected_type_name = write_type_name_temp(w, type); - bool sealed = type.Flags().Sealed(); - - w.write(R"( -public static unsafe class %Marshaller -{ -public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(% value) -{ -% -return default; -} - -public static %? ConvertToManaged(void* value) -{ -return (%?)%.ConvertToManaged<%ComWrappersCallback>(value); -} -} -)", - type.TypeName(), - projected_type_name, - bind([&](writer& w) - { - if (sealed) - { - // For projected sealed runtime classes, the RCW type is always unwrappable (as the - // type can never be subclassed), so we can use UnwrapObjectReferenceUnsafe directly - // instead of going through the TryUnwrapObjectReference check. - w.write(R"( -if (value is not null) -{ -return WindowsRuntimeComWrappersMarshal.UnwrapObjectReferenceUnsafe(value).AsValue(); -} -)"); - } - else - { - auto default_type_semantics = get_type_semantics(get_default_interface(type)); - auto default_interface_typedef = for_typedef(w, default_type_semantics, [&](auto&& iface) { return iface; }); - if (!is_exclusive_to(default_interface_typedef)) - { - w.write(R"( -if (value is IWindowsRuntimeInterface<%> windowsRuntimeInterface) -{ -return windowsRuntimeInterface.GetInterface(); -} -)", - bind(default_type_semantics, typedef_name_type::Projected, false)); - } - else - { - w.write(R"( -if (value is not null) -{ -return value.GetDefaultInterface(); -} -)"); - } - } - }), - projected_type_name, - projected_type_name, - sealed ? "WindowsRuntimeObjectMarshaller" : "WindowsRuntimeUnsealedObjectMarshaller", - type.TypeName()); - } - - void write_class_comwrappers_marshaller_attribute(writer& w, TypeDef const& type) - { - auto default_type_semantics = get_type_semantics(get_default_interface(type)); - auto marshaling_type = get_marshaling_type_name(type); - - w.write(R"( -file sealed unsafe class %ComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute -{% -public override object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) -{ -WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReference( - externalComObject: value, - iid: %, - marshalingType: %, - wrapperFlags: out wrapperFlags); - -return new %(valueReference); -} -} -)", - type.TypeName(), - bind([&](writer& w) { - for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) - { - if (size(interface_type.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, interface_type, true); - } - }); - }), - bind(default_type_semantics), - marshaling_type, - bind(type, typedef_name_type::Projected, true)); - } - - void write_class_comwrappers_callback(writer& w, TypeDef const& type) - { - auto default_type_semantics = get_type_semantics(get_default_interface(type)); - auto marshaling_type = get_marshaling_type_name(type); - - // For sealed, we know the runtime class name is this class, while for unsealed, we check. - if (type.Flags().Sealed()) - { - w.write(R"( -file sealed unsafe class %ComWrappersCallback : IWindowsRuntimeObjectComWrappersCallback -{% -public static object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) -{ -WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe( - externalComObject: value, - iid: %, - marshalingType: %, - wrapperFlags: out wrapperFlags); - -return new %(valueReference); -} -} -)", - type.TypeName(), - bind([&](writer& w) { - for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) - { - if (size(interface_type.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, interface_type, true); - } - }); - }), - bind(default_type_semantics), - marshaling_type, - bind(type, typedef_name_type::Projected, true)); - } - else - { - w.write(R"( -file sealed unsafe class %ComWrappersCallback : IWindowsRuntimeUnsealedObjectComWrappersCallback -{% -public static unsafe bool TryCreateObject( - void* value, - ReadOnlySpan runtimeClassName, - [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out object? wrapperObject, - out CreatedWrapperFlags wrapperFlags) -{ -if (runtimeClassName.SequenceEqual("%".AsSpan())) -{ - WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe( - externalComObject: value, - iid: %, - marshalingType: %, - wrapperFlags: out wrapperFlags); - - wrapperObject = new %(valueReference); - return true; -} - -wrapperObject = null; -wrapperFlags = CreatedWrapperFlags.None; -return false; -} - -public static unsafe object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) -{ -WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe( - externalComObject: value, - iid: %, - marshalingType: %, - wrapperFlags: out wrapperFlags); - -return new %(valueReference); -} -} -)", - type.TypeName(), - bind([&](writer& w) { - for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) - { - if (size(interface_type.GenericParam()) != 0) - { - write_unsafe_accessor_for_iid(w, interface_type, true); - } - }); - }), - // TryCreateObject - bind(type, typedef_name_type::NonProjected, true), - bind(default_type_semantics), - marshaling_type, - bind(type, typedef_name_type::Projected, true), - // CreateObject - bind(default_type_semantics), - marshaling_type, - bind(type, typedef_name_type::Projected, true)); - } - } - - void write_abi_class(writer& w, TypeDef const& type) - { - if (is_static(type)) - { - return; - } - - w.write("#nullable enable\n"); - if (settings.component) - { - write_component_class_marshaller(w, type); - write_authoring_metadata_type(w, type); - } - else - { - write_class_marshaller(w, type); - write_class_comwrappers_marshaller_attribute(w, type); - write_class_comwrappers_callback(w, type); - } - w.write("#nullable disable\n"); - } - - void write_delegate(writer& w, TypeDef const& type) - { - if (settings.component) - { - return; - } - - method_signature signature{ get_delegate_invoke(type) }; - w.write(R"( -%%%%% delegate % %(%); -)", - bind(type), - bind(type, false), - bind(type), - bind([&](writer& w) - { - if (!settings.reference_projection) - { - write_guid_attribute(w, type); - w.write("\n"); - } - }), - internal_accessibility(), - bind(signature), - bind(type, typedef_name_type::Projected, false), - bind_list(", ", signature.params())); - } - - void write_delegate_impl(writer& w, TypeDef const& type) - { - auto method = get_delegate_invoke(type); - method_signature signature{ method }; - - w.write(R"( -internal static unsafe class %Impl -{ - [FixedAddressValueType] - private static readonly %Vftbl Vftbl; - - static %Impl() - { - *(IUnknownVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IUnknownVftbl*)IUnknownImpl.Vtable; - - Vftbl.Invoke = &Invoke; - } - - public static nint Vtable - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)Unsafe.AsPointer(in Vftbl); - } - - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] - private static int Invoke(%) - { - % - } - - public static ref readonly Guid IID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref %; - } -} -)", - type.TypeName(), - type.TypeName(), - type.TypeName(), - bind(signature), - bind( - signature, - w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).%%", - bind(type, typedef_name_type::Projected, false), - method.Name(), - "(%)"), - false), - bind(type) - ); - - } - void write_delegate_comwrappers_callback(writer& w, TypeDef const& type) - { - w.write(R"( -file abstract unsafe class %ComWrappersCallback : IWindowsRuntimeObjectComWrappersCallback -{ - /// - public static object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) - { - WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe( - externalComObject: value, - iid: in %, - wrapperFlags: out wrapperFlags); - - return new %(valueReference.%Invoke); - } -} -)", - type.TypeName(), - bind(type), - bind(type, typedef_name_type::Projected, false), - type.TypeName() - ); - } - - - void write_delegate_com_wrappers_marshaller_attribute_impl(writer& w, TypeDef const& type) - { - auto name = type.TypeName(); - w.write( -R"(internal sealed unsafe class %ComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute -{ - /// - public override void* GetOrCreateComInterfaceForObject(object value) - { - return WindowsRuntimeComWrappersMarshal.GetOrCreateComInterfaceForObject(value, CreateComInterfaceFlags.TrackerSupport); - } - - /// - public override ComInterfaceEntry* ComputeVtables(out int count) - { - count = sizeof(DelegateReferenceInterfaceEntries) / sizeof(ComInterfaceEntry); - - return (ComInterfaceEntry*)Unsafe.AsPointer(in %InterfaceEntriesImpl.Entries); - } - - /// - public override object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) - { - wrapperFlags = CreatedWrapperFlags.NonWrapping; - return WindowsRuntimeDelegateMarshaller.UnboxToManaged<%ComWrappersCallback>(value, in %)!; - } -} - -)", - name, - name, - name, - bind(type)); - } - - void write_delegate_marshaller(writer& w, TypeDef const& type) - { - if (is_exclusive_to(type)) - { - return; - } - - auto projected_type = w.write_temp("%", bind(type, typedef_name_type::Projected, false)); - w.write(R"( -public static unsafe class %Marshaller -{ - public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(% value) - { - return WindowsRuntimeDelegateMarshaller.ConvertToUnmanaged(value, in %); - } - #nullable enable - public static %? ConvertToManaged(void* value) - { - return (%?)WindowsRuntimeDelegateMarshaller.ConvertToManaged<%ComWrappersCallback>(value); - } - #nullable disable -} -)", - type.TypeName(), - projected_type, - bind(type), - projected_type, - projected_type, - type.TypeName() - ); - } - - void write_native_delegate(writer& w, TypeDef const& type) - { - auto method = get_delegate_invoke(type); - method_signature signature{ method }; - auto generic_abi_types = get_generic_abi_types(w, signature); - bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - - w.write(R"( -public static unsafe class %NativeDelegate -{ - public static % %Invoke(this WindowsRuntimeObjectReference objectReference%%) - { - using WindowsRuntimeObjectReferenceValue objectValue = objectReference.AsValue(); - void* ThisPtr = objectValue.GetThisPtrUnsafe(); - var abiInvoke = ((%Vftbl*)*(void***)ThisPtr)->Invoke; - % - } -} -)", - type.TypeName(), - bind(signature), - type.TypeName(), - signature.has_params() ? ", " : "", - bind_list(", ", signature.params()), - type.TypeName(), - bind(signature, "abiInvoke", "_nativeDelegate", have_generic_params, false, is_noexcept(method), false) - ); - } - - void write_delegate_vtbl(writer& w, TypeDef const& type) - { - method_signature signature{ get_delegate_invoke(type) }; - w.write(R"( -[StructLayout(LayoutKind.Sequential)] -internal unsafe struct %Vftbl -{ - public delegate* unmanaged[MemberFunction] QueryInterface; - public delegate* unmanaged[MemberFunction] AddRef; - public delegate* unmanaged[MemberFunction] Release; - public delegate* unmanaged[MemberFunction]<%, int> Invoke; -} -)", - type.TypeName(), - bind(signature)); - } - - void write_abi_delegate(writer& w, TypeDef const& type) - { - write_delegate_marshaller(w, type); - write_delegate_vtbl(w, type); - write_native_delegate(w, type); - write_delegate_comwrappers_callback(w, type); - write_delegates_interface_entries_impl(w, type); - write_delegate_com_wrappers_marshaller_attribute_impl(w, type); - write_delegate_impl(w, type); - write_reference_impl(w, type); - - if (settings.component) - { - write_authoring_metadata_type(w, type); - } - } - - void write_constant(writer& w, Constant const& value) - { - switch (value.Type()) - { - case ConstantType::Int32: - w.write_printf("%#0x", value.ValueInt32()); - break; - case ConstantType::UInt32: - w.write_printf("%#0x", value.ValueUInt32()); - break; - } - } - - void write_enum(writer& w, TypeDef const& type) - { - if (settings.component) - { - return; - } - - auto enum_underlying_type = is_flags_enum(type) ? "uint" : "int"; - - w.write( -R"( -%%%%%%% enum % : % -{ -)", - is_flags_enum(type) ? "[FlagsAttribute]\n" : "", - bind(type), - bind(type), - bind(type, true), - bind(type), - bind(type), - (settings.internal) ? "internal" : "public", - bind(type, typedef_name_type::Projected, false), enum_underlying_type); - { - for (auto&& field : type.FieldList()) - { - if (auto constant = field.Constant()) - { - w.write("%% = unchecked((%)%),\n", - bind(field.CustomAttribute()), - field.Name(), enum_underlying_type, bind(constant)); - } - } - } - w.write("}\n\n"); - } - - void write_abi_enum(writer& w, TypeDef const& type) - { - write_struct_and_enum_marshaller_class(w, type); - write_interface_entries_impl(w, type); - write_struct_and_enum_com_wrappers_marshaller_attribute_impl(w, type); - write_reference_impl(w, type); - - if (settings.component) - { - write_authoring_metadata_type(w, type); - } - } - - void write_struct(writer& w, TypeDef const& type) - { - if (settings.component) - { - return; - } - - auto projection_name = w.write_temp("%", bind(type)); - struct field_info - { - std::string type; - std::string name; - std::string param_name; - bool is_interface; - }; - std::vector fields; - for (auto&& field : type.FieldList()) - { - auto semantics = get_type_semantics(field.Signature().Type()); - field_info field_info{}; - field_info.type = w.write_temp("%", [&](writer& w) { write_projection_type(w, semantics); }); - field_info.name = field.Name(); - field_info.param_name = to_camel_case(field_info.name); - if (auto td = std::get_if(&semantics)) - { - field_info.is_interface = get_category(*td) == category::interface_type; - } - else if (auto gti = std::get_if(&semantics)) - { - field_info.is_interface = get_category(gti->generic_type) == category::interface_type; - } - fields.emplace_back(field_info); - } - - // struct - w.write("%%%%public% struct %: IEquatable<%>\n{\n", - bind(type), - bind(type), - bind(type), - bind(type), - has_addition_to_type(type) ? " partial" : "", - type.TypeName(), - type.TypeName()); - - // ctor (use camelCase parameter names for cleaner public API) - w.write("public %(%)\n{\n%\n}\n", - type.TypeName(), - bind_list([](writer& w, auto&& field) - { - w.write("% %", field.type, bind(field.param_name)); - }, ", ", fields), - bind_each([](writer& w, auto&& field) - { - // When the param name matches the field name (i.e. to_camel_case couldn't - // change the casing), qualify the field with 'this.' to disambiguate. - if (field.name == field.param_name) - { - w.write("this.% = %; ", field.name, bind(field.param_name)); - } - else - { - w.write("% = %; ", field.name, bind(field.param_name)); - } - }, fields)); - - // properties - w.write("%", - bind_each([](writer& w, auto&& field) - { - w.write("public % %\n{\n", field.type, field.name); - w.write("readonly get; set;\n"); - w.write("}\n"); - }, fields)); - - // == - w.write("public static bool operator ==(% x, % y) => %;\n", - projection_name, - projection_name, - bind_list([](writer& w, auto&& field) - { - w.write("x.% == y.%", field.name, field.name); - }, " && ", fields)); - - // != - w.write("public static bool operator !=(% x, % y) => !(x == y);\n", - projection_name, - projection_name); - - // equals - w.write("public bool Equals(% other) => this == other;\n", projection_name); - w.write("public override bool Equals(object obj) => obj is % that && this == that;\n", projection_name); - - // hashcode - w.write("public override int GetHashCode() => %;\n", - bind_list([](writer& w, auto&& field) - { - w.write("%.GetHashCode()", field.name); - }, " ^ ", fields)); - - // end class - w.write("}\n\n"); - } - - - void write_abi_struct(writer& w, TypeDef const& type) - { - if (!is_type_blittable(type)) - { - // For component structs, cswinrtgen handles the vtable and comwrappers attribute. - if (settings.component) - { - write_winrt_metadata_typename_attribute(w, type); - write_winrt_mapped_type_attribute(w, type); - } - else - { - write_comwrapper_marshaller_attribute(w, type); - } - - w.write("%% unsafe struct %\n{\n", - bind(type), - internal_accessibility(), - bind(type, typedef_name_type::ABI, false)); - - for (auto&& field : type.FieldList()) - { - w.write("public "); - write_abi_type(w, get_type_semantics(field.Signature().Type())); - w.write(" %;\n", field.Name()); - } - w.write("}\n\n"); - } - else if (settings.component) - { - write_authoring_metadata_type(w, type); - } - - write_struct_and_enum_marshaller_class(w, type); - write_interface_entries_impl(w, type); - - if (!settings.component) - { - write_struct_and_enum_com_wrappers_marshaller_attribute_impl(w, type); - } - write_reference_impl(w, type); - } - - void write_factory_class_inheritance(writer& w, TypeDef const& type) - { - auto delimiter{ ", " }; - auto write_delimiter = [&]() - { - w.write(delimiter); - }; - - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if ((factory.activatable || factory.statics) && factory.type) - { - write_delimiter(); - w.write("%", bind(factory.type, typedef_name_type::CCW, false)); - } - } - } - - void write_factory_activatable_method(writer& w, MethodDef const& method, std::string_view activatable_type) - { - method_signature signature{ method }; - w.write(R"( -public % %(%) => new %(%); -)", -activatable_type, -method.Name(), -bind_list(", ", signature.params()), -activatable_type, -bind_list(", ", signature.params()) -); - } - - void write_factory_class_members(writer& w, TypeDef const& type) - { - auto delimiter{ ", " }; - auto write_delimiter = [&]() - { - w.write(delimiter); - }; - - auto projected_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if (factory.type) - { - if (factory.activatable) - { - w.write_each(factory.type.MethodList(), projected_type_name); - } - else if (factory.statics) - { - w.write_each(factory.type.MethodList(), projected_type_name, ""sv); - w.write_each(factory.type.PropertyList(), projected_type_name, ""sv); - w.write_each(factory.type.EventList(), projected_type_name, ""sv); - } - } - } - } - - - void write_factory_class(writer& w, TypeDef const& type) - { - auto factory_type_name = w.write_temp("%ServerActivationFactory", type.TypeName()); - auto is_activatable = !is_static(type) && has_default_constructor(type); - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); - - auto activate_instance_body = is_activatable - ? w.write_temp("return new %();", type_name) - : "throw new NotImplementedException();"; - - w.write(R"( -internal sealed class % : global::WindowsRuntime.InteropServices.IActivationFactory% -{ -static %() -{ - global::System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(%).TypeHandle); -} - -public static unsafe void* Make() -{ - return global::WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeInterfaceMarshaller - .ConvertToUnmanaged(_factory, in global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IActivationFactory) - .DetachThisPtrUnsafe(); -} - -private static readonly % _factory = new(); - -public object ActivateInstance() -{ - % -} - -% -} -)", - factory_type_name, - bind(type), - factory_type_name, - type_name, - factory_type_name, - activate_instance_body, - bind(type) - ); - } - - void write_module_activation_factory(writer& w, std::map> const& typesByModule) - { - w.write(R"( -using System; -)"); - for (auto&& [module_name, types] : typesByModule) - { - w.write(R"( -namespace ABI.% -{ -public static class ManagedExports -{ -public static unsafe void* GetActivationFactory(ReadOnlySpan activatableClassId) -{ -switch (activatableClassId) -{ -% -default: - return null; -} -} -} -} -)", - module_name, - bind_each([](writer& w, TypeDef const& type) - { - w.write(R"( -case "%.%": - return %ServerActivationFactory.Make(); -)", - type.TypeNamespace(), - type.TypeName(), - bind(type, typedef_name_type::CCW, true) - ); - }, - types - ) - ); - } - } - - void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics) - { - if (!std::holds_alternative(eventTypeSemantics)) - { - return; - } - for_typedef(w, eventTypeSemantics, [&](TypeDef const&) - { - std::vector genericArgs; - auto arg_count = std::get(eventTypeSemantics).generic_args.size(); - for (int i = 0; i < (int) arg_count; ++i ) - { - auto semantics = w.get_generic_arg_scope(i).first; - if (std::holds_alternative(semantics)) - { - genericArgs.push_back(w.write_temp("%", bind(i))); - } - } - if (genericArgs.size() == 0) - { - return; - } - w.write("<%>", bind_list([](writer& w, auto&& value) - { - w.write(value); - }, ", "sv, genericArgs)); - }); - } - - void write_event_source_subclass(writer& w, cswinrt::type_semantics eventTypeSemantics) - { - auto genericInstantiationInitialization = w.write_temp("%", bind(eventTypeSemantics, true)); - - auto abiTypeName = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::ABI, true)); - for_typedef(w, eventTypeSemantics, [&](TypeDef const& eventType) - { - if ((eventType.TypeNamespace() == "Windows.Foundation" || eventType.TypeNamespace() == "System") && eventType.TypeName() == "EventHandler`1") - { - return; - } - - auto eventTypeCode = w.write_temp("%", bind(eventType, typedef_name_type::Projected, false)); - auto invokeMethodSig = get_event_invoke_method_signature(eventType); - w.write(R"( -internal sealed unsafe class %% : global::ABI.WinRT.Interop.EventSource<%> -{ -% - -internal %(IObjectReference obj, int vtableIndexForAddHandler) : base(obj, vtableIndexForAddHandler) -{ -% -} - -protected override ObjectReferenceValue CreateMarshaler(% handler) => -%.CreateMarshaler2(handler); - -protected override global::ABI.WinRT.Interop.EventSourceState<%> CreateEventSourceState() => -new EventState(ObjectReference.ThisPtr, Index); - -% -private sealed class EventState : global::ABI.WinRT.Interop.EventSourceState<%> -{ -public EventState(System.IntPtr obj, int index) -: base(obj, index) -{ -} - -protected override % GetEventInvoke() -{ -return (%) => -{ -var targetDelegate = TargetDelegate; -if (targetDelegate is null) -{% -return %; -} -%targetDelegate.Invoke(%); -}; -} -} -} -)", -bind(eventTypeSemantics), -bind(eventTypeSemantics), -eventTypeCode, // EventSource<%> -genericInstantiationInitialization, -bind(eventTypeSemantics), -genericInstantiationInitialization == "" ? "" : "_ = initialized;", -eventTypeCode, // % handler -abiTypeName, -eventTypeCode, // EventSourceState<%> -settings.netstandard_compat ? "" : "[global::WinRT.WinRTExposedType]", -eventTypeCode, // EventSourceState<%> -eventTypeCode, // % GetEventInvoke() -bind(invokeMethodSig), -bind(invokeMethodSig), -bind(invokeMethodSig), -bind(invokeMethodSig), -bind(invokeMethodSig)); -}); - } - - void write_temp_delegate_event_source_subclass(writer& w, TypeDef const& type) - { - auto method = get_delegate_invoke(type); - method_signature signature{ method }; - - w.write(R"( -public sealed unsafe class %EventSource : EventSource<%> -{ - /// - public %EventSource(WindowsRuntimeObjectReference nativeObjectReference, int index) - : base(nativeObjectReference, index) - { - } - - /// - protected override WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(% value) - { - return %Marshaller.ConvertToUnmanaged(value); - } - - /// - protected override EventSourceState<%> CreateEventSourceState() - { - return new EventState(GetNativeObjectReferenceThisPtrUnsafe(), Index); - } - - private sealed class EventState : EventSourceState<%> - { - /// - public EventState(void* thisPtr, int index) - : base(thisPtr, index) - { - } - - /// - protected override % GetEventInvoke() - { - return (%) => TargetDelegate.Invoke(%); - } - } -} - )", - type.TypeName(), - bind(type, typedef_name_type::Projected, true), - type.TypeName(), - bind(type, typedef_name_type::Projected, true), - type.TypeName(), - bind(type, typedef_name_type::Projected, true), - bind(type, typedef_name_type::Projected, true), - bind(type, typedef_name_type::Projected, true), - bind_list(", ", signature.params()), - bind_list(", ", signature.params()) - ); - } - - void write_temp_class_event_source_subclass(writer& w, TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) - { - for (auto&& ii : classType.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& interfaceType) - { - for (auto&& eventObj : interfaceType.EventList()) - { - auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); - auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); - auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); - typeNameToDefinitionMap[eventTypeCode] = eventClass; - } - }); - } - } - - void write_temp_interface_event_source_subclass(writer& w, TypeDef const& interfaceType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) - { - for (auto&& eventObj : interfaceType.EventList()) - { - auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); - auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); - auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); - typeNameToDefinitionMap[eventTypeCode] = eventClass; - } - } - - void add_base_type_entry(TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToBaseTypeMap) - { - writer w(""); - auto base_type = get_type_semantics(classType.Extends()); - bool has_base_type = !std::holds_alternative(base_type); - if (has_base_type) - { - int numChars = (int)strlen("global::"); - auto&& typeName = w.write_temp("%", bind(classType, typedef_name_type::Projected, true)).substr(numChars); - auto&& baseTypeName = w.write_temp("%", bind(base_type, typedef_name_type::Projected, true)).substr(numChars); - typeNameToBaseTypeMap[typeName] = baseTypeName; - } - } - - void add_metadata_type_entry(TypeDef const& type, concurrency::concurrent_unordered_map& authoredTypeNameToMetadataTypeNameMap) - { - if (settings.component) - { - if ((get_category(type) == category::class_type && is_static(type)) || - (get_category(type) == category::interface_type && is_exclusive_to(type))) - { - return; - } - - writer w(""); - auto&& typeName = w.write_temp("%", bind(type, typedef_name_type::Projected, true)); - auto&& metadataTypeName = w.write_temp("%", bind(type, typedef_name_type::CCW, true)); - authoredTypeNameToMetadataTypeNameMap[typeName] = metadataTypeName; - } - } - - // Checking for if this is an ABI delegate that will need to be code generated or - // whether it is one that we manually already added in Projections.cs such as for - // classes where the ABI type is IntPtr and we can handle generically. - bool is_abi_delegate_required_for_type(type_semantics const& semantics) - { - return call(semantics, - [&](guid_type) - { - return true; - }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::enum_type: - case category::struct_type: - return true; - default: - return false; - } - }, - [&](fundamental_type type) - { - return type != fundamental_type::String; - }, - [](auto) - { - return false; - }); - } - - void add_abi_delegates_for_type(std::string_view typeNamespace, std::string_view typeName, std::vector generics, concurrency::concurrent_unordered_set& abiDelegateEntries) - { - writer w; - if (typeNamespace == "Windows.Foundation" || typeNamespace == "Windows.Foundation.Collections") - { - if (typeName == "IIterator`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Current_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - } - } - else if (typeName == "IKeyValuePair`2") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Key_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Key_%(IntPtr thisPtr, %* __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(IntPtr), typeof(%*), typeof(int) }", abiType) - }); - } - - if (is_abi_delegate_required_for_type(generics[1])) - { - auto abiType = w.write_temp("%", bind(generics[1])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Value_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Value_%(IntPtr thisPtr, %* __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(IntPtr), typeof(%*), typeof(int) }", abiType) - }); - } - } - else if (typeName == "IMapView`2") - { - if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) - { - auto keyAbiType = w.write_temp("%", bind(generics[0])); - auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); - - auto valueAbiType = w.write_temp("%", bind(generics[1])); - auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), - w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) - }); - - if (is_abi_delegate_required_for_type(generics[0])) - { - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_has_key_%", escapedKeyAbiType), - w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) - }); - } - } - } - else if (typeName == "IMap`2") - { - if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) - { - auto keyAbiType = w.write_temp("%", bind(generics[0])); - auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); - - auto valueAbiType = w.write_temp("%", bind(generics[1])); - auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), - w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) - }); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_insert_%_%", escapedKeyAbiType, escapedValueAbiType), - w.write_temp("internal unsafe delegate int _insert_%_%(void* thisPtr, % key, % value, out byte replaced);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) - }); - - if (is_abi_delegate_required_for_type(generics[0])) - { - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_has_key_%", escapedKeyAbiType), - w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) - }); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_remove_%", escapedKeyAbiType), - w.write_temp("internal unsafe delegate int _remove_%(void* thisPtr, % key);", escapedKeyAbiType, keyAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", keyAbiType) - }); - } - } - } - else if (typeName == "IVectorView`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_at_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_index_of_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) - }); - - // GetEnumerator in IVectorView - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Current_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - } - } - else if (typeName == "IVector`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_at_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_index_of_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) - }); - - // SetAt / InsertAt - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_set_at_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _set_at_%(void* thisPtr, uint index, % value);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%), typeof(int) }", abiType) - }); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_append_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _append_%(void* thisPtr, % value);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", abiType) - }); - - // GetEnumerator in IVector - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Current_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - } - } - else if (typeName == "EventHandler`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_invoke_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr sender, % args);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) - }); - } - } - else if (typeName == "IReference`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_Value_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_Value_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - } - } - else if (typeName == "IMapChangedEventArgs`1" || - typeName == "IAsyncOperation`1" || - typeName == "IAsyncOperationWithProgress`2") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_get_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _get_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) - }); - } - - // Add ABI delegates for AsyncOperationProgressHandler as it is referenced in a property of IAsyncOperationWithProgress - // which isn't handled separately. - if (typeName == "IAsyncOperationWithProgress`2" && is_abi_delegate_required_for_type(generics[1])) - { - add_abi_delegates_for_type("Windows.Foundation", "AsyncOperationProgressHandler`2", generics, abiDelegateEntries); - } - } - else if (typeName == "TypedEventHandler`2") - { - if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) - { - auto senderAbiType = w.write_temp("%", bind(generics[0])); - auto escapedSenderAbiType = escape_type_name_for_identifier(senderAbiType); - - auto resultAbiType = w.write_temp("%", bind(generics[1])); - auto escapedResultAbiType = escape_type_name_for_identifier(resultAbiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_invoke_%_%", escapedSenderAbiType, escapedResultAbiType), - w.write_temp("internal unsafe delegate int _invoke_%_%(void* thisPtr, % sender, % args);", escapedSenderAbiType, escapedResultAbiType, senderAbiType, resultAbiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(int) }", senderAbiType, resultAbiType) - }); - } - } - else if (typeName == "AsyncOperationProgressHandler`2") - { - if (is_abi_delegate_required_for_type(generics[1])) - { - auto abiType = w.write_temp("%", bind(generics[1])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_invoke_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) - }); - } - } - else if (typeName == "AsyncActionProgressHandler`1") - { - if (is_abi_delegate_required_for_type(generics[0])) - { - auto abiType = w.write_temp("%", bind(generics[0])); - auto escapedAbiType = escape_type_name_for_identifier(abiType); - - abiDelegateEntries.insert(generic_abi_delegate - { - w.write_temp("_invoke_%", escapedAbiType), - w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), - w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) - }); - } - } - } - } - - void add_if_generic_type_reference(cswinrt::type_semantics const& typeSemantics, bool isArray, concurrency::concurrent_unordered_set& abiDelegateEntries) - { - call(typeSemantics, - [&](generic_type_instance const& type) - { - // Handle generics of generics where their delegate entries should also be added. - for (auto& arg : type.generic_args) - { - // None of the generics should be an array given WinRT doens't support that. - add_if_generic_type_reference(arg, false, abiDelegateEntries); - } - - add_abi_delegates_for_type(type.generic_type.TypeNamespace(), type.generic_type.TypeName(), type.generic_args, abiDelegateEntries); - }, - [&](type_definition const& type) - { - switch (get_category(type)) - { - case category::struct_type: - { - // Struct fields can have IReference generics which we should generate delegates for. - for (auto&& field : type.FieldList()) - { - auto fieldType = field.Signature().Type(); - add_if_generic_type_reference(get_type_semantics(fieldType), fieldType.is_szarray(), abiDelegateEntries); - } - break; - } - } - - // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. - if (isArray) - { - std::vector genericArgs = { typeSemantics }; - add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); - } - }, - [&](auto) - { - // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. - if (isArray) - { - std::vector genericArgs = { typeSemantics }; - add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); - } - } - ); - } - - void add_generic_type_references_in_interface_type(TypeDef const& interfaceType, concurrency::concurrent_unordered_set& abiDelegateEntries) - { - for (auto&& method : interfaceType.MethodList()) - { - if (is_special(method)) - { - continue; - } - - method_signature signature{ method }; - if (signature.has_params()) - { - for (auto&& param : signature.params()) - { - add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); - } - } - - if (auto& returnSignature = signature.return_signature()) - { - add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); - } - } - - for (auto&& prop : interfaceType.PropertyList()) - { - add_if_generic_type_reference(get_type_semantics(prop.Type().Type()), prop.Type().Type().is_szarray(), abiDelegateEntries); - } - - for (auto&& evt : interfaceType.EventList()) - { - add_if_generic_type_reference(get_type_semantics(evt.EventType()), false, abiDelegateEntries); - } - } - - void add_generic_type_references_in_type(TypeDef const& type, concurrency::concurrent_unordered_set& abiDelegateEntries) - { - switch (get_category(type)) - { - case category::delegate_type: - { - method_signature signature{ get_delegate_invoke(type) }; - if (signature.has_params()) - { - for (auto&& param : signature.params()) - { - add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); - } - } - - if (auto& returnSignature = signature.return_signature()) - { - add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); - } - - break; - } - case category::interface_type: - add_generic_type_references_in_interface_type(type, abiDelegateEntries); - break; - } - } - - bool has_generic_type_instantiations() - { - return generic_type_instances.size() != 0; - } - - void write_generic_type_instantiation(writer& w, generic_type_instance instance, std::vector& rcwFunctions, std::vector& vtableFunctions) - { - auto get_invoke_info = [&](MethodDef const& method, bool isDelegate = false) - { - return w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", - bind([&](writer& w) { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_abi_type(w, w.get_generic_arg_scope(index).first); - }); - - write_abi_parameter_types_pointer(w, method_signature{ method }); - }), - isDelegate ? 3 : get_vmethod_index(instance.generic_type, method) + INSPECTABLE_METHOD_COUNT); - }; - - if (get_category(instance.generic_type) == category::delegate_type) - { - auto method = get_event_invoke_method(instance.generic_type); - method_signature signature{ method }; - - auto guard{ w.push_generic_args(instance) }; - - if (abi_signature_has_generic_parameters(w, signature)) - { - rcwFunctions.emplace_back(method.Name()); - - auto invoke_target = get_invoke_info(method, true); - w.write(R"( -private static unsafe % %(IObjectReference _obj%%) -{ -var ThisPtr = _obj.ThisPtr; -%} -)", - bind(signature), - method.Name(), - signature.has_params() ? ", " : "", - bind_list(", ", signature.params()), - bind(signature, invoke_target, "_obj", false, false, is_noexcept(method), true)); - } - - vtableFunctions.emplace_back(method.Name()); - - w.write(R"( -[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -private static unsafe int Do_Abi_Invoke(%) -{ -% -} -)", - [&](writer& w) { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_abi_type(w, w.get_generic_arg_scope(index).first); - }); - - write_abi_parameters(w, signature); - }, - [&](writer& w) { - auto invoke = w.write_temp( - "%%.Abi_Invoke(thisPtr, %)", - bind(instance.generic_type, typedef_name_type::StaticAbiClass), - [&](writer& w) - { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_projection_type(w, w.get_generic_arg_scope(index).first); - w.write(", "); - write_abi_type(w, w.get_generic_arg_scope(index).first); - }); - - write_type_params(w, instance.generic_type); - }, - "%"); - write_managed_method_call(w, signature, invoke, true); - }); - } - else - { - for (auto&& method : instance.generic_type.MethodList()) - { - method_signature signature{ method }; - - if (!projected_signature_has_generic_parameters(w, signature)) - { - continue; - } - - // Adding RCW function names here including the synthesized methods for - // properties so that we have them in the right vtable order even if we - // don't write them here. - if (!(is_special(method) && - (starts_with(method.Name(), "add_") || - starts_with(method.Name(), "remove_")))) - { - rcwFunctions.emplace_back(method.Name()); - } - - if (is_special(method)) - { - continue; - } - - auto guard{ w.push_generic_args(instance) }; - - auto invoke_target = get_invoke_info(method); - w.write(R"( -private static unsafe % %(IObjectReference _obj%%) -{ -var ThisPtr = _obj.ThisPtr; -%} -)", - bind(signature), - method.Name(), - signature.has_params() ? ", " : "", - bind_list(", ", signature.params()), - bind(signature, invoke_target, "_obj", false, false, is_noexcept(method), true)); - } - - for (auto&& prop : instance.generic_type.PropertyList()) - { - auto guard{ w.push_generic_args(instance) }; - auto [getter, setter] = get_property_methods(prop); - - if (getter && projected_signature_has_generic_parameters(w, method_signature{ getter })) - { - auto invoke_target = get_invoke_info(getter); - auto signature = method_signature(getter); - auto marshalers = get_abi_marshalers(w, signature, false, prop.Name(), false, true); - w.write(R"(private static unsafe % %(IObjectReference _obj) -{ -var ThisPtr = _obj.ThisPtr; -%} -)", - write_prop_type(w, prop), - getter.Name(), - bind(invoke_target, "_obj", false, marshalers, is_noexcept(prop))); - } - if (setter && projected_signature_has_generic_parameters(w, method_signature{ setter })) - { - auto invoke_target = get_invoke_info(setter); - auto signature = method_signature(setter); - auto marshalers = get_abi_marshalers(w, signature, false, prop.Name(), false, true); - marshalers[0].param_name = "value"; - w.write(R"(private static unsafe void %(IObjectReference _obj, % value) -{ -var ThisPtr = _obj.ThisPtr; -%} -)", - setter.Name(), - write_prop_type(w, prop), - bind(invoke_target, "_obj", false, marshalers, is_noexcept(prop))); - } - w.write("\n"); - } - } - } - - generic_type_instance ConvertGenericTypeInstanceToConcreteType(writer& w, const generic_type_instance& generic_instance) - { - generic_type_instance converted = generic_instance; - for (size_t idx = 0; idx < generic_instance.generic_args.size(); idx++) - { - auto& semantics = generic_instance.generic_args[idx]; - if (auto gti = std::get_if(&semantics)) - { - converted.generic_args[idx] = w.get_generic_arg_scope(gti->index).first; - } - else if (auto instance = std::get_if(&semantics)) - { - converted.generic_args[idx] = ConvertGenericTypeInstanceToConcreteType(w, *instance); - } - } - - return converted; - } - - void write_generic_type_instantiations(writer& w) - { - // Go through all the generic types and write instantiation classes for them. - // While writing them, their implementations might also reference generic types - // which may also need instantiation classes if one hasn't been generated already. - concurrency::concurrent_unordered_set written_generic_type_instances; - bool types_written = true; - while (generic_type_instances.size() != 0 && types_written) - { - types_written = false; - concurrency::concurrent_unordered_set current_generic_type_instances = generic_type_instances; - generic_type_instances = concurrency::concurrent_unordered_set(); - for (auto& instance : current_generic_type_instances) - { - if (written_generic_type_instances.find(instance) != written_generic_type_instances.end()) - { - continue; - } - written_generic_type_instances.insert(instance); - types_written = true; - - std::vector rcwFunctions, vtableFunctions; - w.write(R"( -internal static class % -{ -private static bool Initialized { get; } = Init(); - -public static bool EnsureInitialized() => Initialized; - -% - -private unsafe static bool Init() -{ -% -% -return true; -} -} -)", - instance.instantiation_class_name, - bind(instance.instance, rcwFunctions, vtableFunctions), - bind([&](writer& w) { - auto guard{ w.push_generic_args(instance.instance) }; - - auto genericInstantiationMethodsClass = w.write_temp("%%", - bind(instance.instance.generic_type, typedef_name_type::StaticAbiClass), - [&](writer& w) - { - writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) - { - write_projection_type(w, w.get_generic_arg_scope(index).first); - w.write(", "); - write_abi_type(w, w.get_generic_arg_scope(index).first); - }); - - write_type_params(w, instance.instance.generic_type); - }); - - if (rcwFunctions.size() != 0 || get_category(instance.instance.generic_type) != category::delegate_type) - { - w.write("%.InitRcwHelper(%);", - genericInstantiationMethodsClass, - bind_list([](writer& w, std::string const& rcwFunction) - { - w.write("&%", rcwFunction); - }, ",\n", rcwFunctions)); - } - - if (get_category(instance.instance.generic_type) == category::delegate_type) - { - w.write("\n%.InitCcw(&Do_Abi_Invoke);", genericInstantiationMethodsClass); - } - }), - bind([&](writer& w) { - auto guard{ w.push_generic_args(instance.instance) }; - - // Initialize any interfaces implemented by this type as they - // can be called by the consumer when using this interface. - for (auto&& iface : instance.instance.generic_type.InterfaceImpl()) - { - write_ensure_generic_type_initialized(w, get_type_semantics(iface.Interface()), false); - } - - // Events don't have an implementation in the generic instantiation classes - // given they use generated event sources and are shared in a projection. - // But this can mean that the instantiation classes for generic events types in - // generic interfaces aren't initialized. This handles those initializations. - for (auto&& evt : instance.instance.generic_type.EventList()) - { - write_ensure_generic_type_initialized(w, get_type_semantics(evt.EventType()), false); - } - })); - } - } - } - -} \ No newline at end of file diff --git a/src/cswinrt/cswinrt.natvis b/src/cswinrt/cswinrt.natvis deleted file mode 100644 index 79affa8f91..0000000000 --- a/src/cswinrt/cswinrt.natvis +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - {i_max(i_FirstTail(), i_SecondTail())} - - i_FirstHead() - i_FirstTail() - i_SecondHead() - i_SecondTail() - - - - \ No newline at end of file diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj deleted file mode 100644 index 9a0abe2129..0000000000 --- a/src/cswinrt/cswinrt.vcxproj +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - 15.0 - {6acfd2b2-e8aa-4cd4-aad8-213ce8bb2637} - Win32Proj - cswinrt - cswinrt - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - Application - Unicode - - - - - - - - - - - - - Console - kernel32.lib;user32.lib;%(AdditionalDependencies);windowsapp.lib;advapi32.lib;shlwapi.lib - - - true - - - - - - - - - - - - - - - - - Create - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters deleted file mode 100644 index 2103816d55..0000000000 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/cswinrt/guid_generator.h b/src/cswinrt/guid_generator.h deleted file mode 100644 index 2ead81ce74..0000000000 --- a/src/cswinrt/guid_generator.h +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// Copied from -// https://github.com/microsoft/cppwinrt/blob/master/strings/base_identity.h -// https://github.com/microsoft/cppwinrt/blob/master/natvis/type_resolver.cpp - -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace cswinrt -{ - constexpr auto sha1_rotl(uint8_t bits, uint32_t word) noexcept - { - return (word << bits) | (word >> (32 - bits)); - } - - constexpr auto sha_ch(uint32_t x, uint32_t y, uint32_t z) noexcept - { - return (x & y) ^ ((~x) & z); - } - - constexpr auto sha_parity(uint32_t x, uint32_t y, uint32_t z) noexcept - { - return x ^ y ^ z; - } - - constexpr auto sha_maj(uint32_t x, uint32_t y, uint32_t z) noexcept - { - return (x & y) ^ (x & z) ^ (y & z); - } - - constexpr std::array process_msg_block(uint8_t const* input, size_t start_pos, std::array const& intermediate_hash) noexcept - { - uint32_t const K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; - std::array W = {}; - - size_t t = 0; - uint32_t temp = 0; - - for (t = 0; t < 16; t++) - { - W[t] = static_cast(input[start_pos + t * 4]) << 24; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 1]) << 16; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 2]) << 8; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 3]); - } - - for (t = 16; t < 80; t++) - { - W[t] = sha1_rotl(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); - } - - uint32_t A = intermediate_hash[0]; - uint32_t B = intermediate_hash[1]; - uint32_t C = intermediate_hash[2]; - uint32_t D = intermediate_hash[3]; - uint32_t E = intermediate_hash[4]; - - for (t = 0; t < 20; t++) - { - temp = sha1_rotl(5, A) + sha_ch(B, C, D) + E + W[t] + K[0]; - E = D; - D = C; - C = sha1_rotl(30, B); - B = A; - A = temp; - } - - for (t = 20; t < 40; t++) - { - temp = sha1_rotl(5, A) + sha_parity(B, C, D) + E + W[t] + K[1]; - E = D; - D = C; - C = sha1_rotl(30, B); - B = A; - A = temp; - } - - for (t = 40; t < 60; t++) - { - temp = sha1_rotl(5, A) + sha_maj(B, C, D) + E + W[t] + K[2]; - E = D; - D = C; - C = sha1_rotl(30, B); - B = A; - A = temp; - } - - for (t = 60; t < 80; t++) - { - temp = sha1_rotl(5, A) + sha_parity(B, C, D) + E + W[t] + K[3]; - E = D; - D = C; - C = sha1_rotl(30, B); - B = A; - A = temp; - } - - return { intermediate_hash[0] + A, intermediate_hash[1] + B, intermediate_hash[2] + C, intermediate_hash[3] + D, intermediate_hash[4] + E }; - } - - template - constexpr std::array process_msg_block(std::array const& input, size_t start_pos, std::array const& intermediate_hash) noexcept - { - return process_msg_block(input.data(), start_pos, intermediate_hash); - } - - constexpr std::array size_to_bytes(size_t size) noexcept - { - return - { - static_cast((size & 0xff00000000000000) >> 56), - static_cast((size & 0x00ff000000000000) >> 48), - static_cast((size & 0x0000ff0000000000) >> 40), - static_cast((size & 0x000000ff00000000) >> 32), - static_cast((size & 0x00000000ff000000) >> 24), - static_cast((size & 0x0000000000ff0000) >> 16), - static_cast((size & 0x000000000000ff00) >> 8), - static_cast((size & 0x00000000000000ff) >> 0) - }; - } - - template - constexpr std::array get_result(std::array const& intermediate_hash, std::index_sequence) noexcept - { - return { static_cast(intermediate_hash[Index >> 2] >> (8 * (3 - (Index & 0x03))))... }; - } - - constexpr auto get_result(std::array const& intermediate_hash) noexcept - { - return get_result(intermediate_hash, std::make_index_sequence<20>{}); - } - - constexpr uint32_t to_guid(uint8_t a, uint8_t b, uint8_t c, uint8_t d) noexcept - { - return (static_cast(d) << 24) | (static_cast(c) << 16) | (static_cast(b) << 8) | static_cast(a); - } - - constexpr uint16_t to_guid(uint8_t a, uint8_t b) noexcept - { - return (static_cast(b) << 8) | static_cast(a); - } - - template - constexpr GUID to_guid(std::array const& arr) noexcept - { - return - { - to_guid(arr[0], arr[1], arr[2], arr[3]), - to_guid(arr[4], arr[5]), - to_guid(arr[6], arr[7]), - { arr[8], arr[9], arr[10], arr[11], arr[12], arr[13], arr[14], arr[15] } - }; - } - - constexpr uint32_t endian_swap(unsigned long value) noexcept - { - return (value & 0xFF000000) >> 24 | (value & 0x00FF0000) >> 8 | (value & 0x0000FF00) << 8 | (value & 0x000000FF) << 24; - } - - constexpr uint16_t endian_swap(unsigned short value) noexcept - { - return (value & 0xFF00) >> 8 | (value & 0x00FF) << 8; - } - - constexpr GUID endian_swap(GUID value) noexcept - { - value.Data1 = endian_swap(value.Data1); - value.Data2 = endian_swap(value.Data2); - value.Data3 = endian_swap(value.Data3); - return value; - } - - constexpr GUID set_named_guid_fields(GUID value) noexcept - { - value.Data3 = static_cast((value.Data3 & 0x0fff) | (5 << 12)); - value.Data4[0] = static_cast((value.Data4[0] & 0x3f) | 0x80); - return value; - } - - template - constexpr std::array to_array(T const* value, std::index_sequence const) noexcept - { - return { value[Index]... }; - } - - template - constexpr auto to_array(std::array const& value) noexcept - { - return value; - } - - template - constexpr auto to_array(char const(&value)[Size]) noexcept - { - return to_array(value, std::make_index_sequence()); - } - - template - constexpr auto to_array(wchar_t const(&value)[Size]) noexcept - { - return to_array(value, std::make_index_sequence()); - } - - template - constexpr std::array concat( - [[maybe_unused]] std::array const& left, - [[maybe_unused]] std::array const& right, - std::index_sequence const, - std::index_sequence const) noexcept - { - return { left[LeftIndex]..., right[RightIndex]... }; - } - - template - constexpr auto concat(std::array const& left, std::array const& right) noexcept - { - return concat(left, right, std::make_index_sequence(), std::make_index_sequence()); - } - - template - constexpr auto concat(std::array const& left, T const(&right)[RightSize]) noexcept - { - return concat(left, to_array(right)); - } - - template - constexpr auto concat(T const(&left)[LeftSize], std::array const& right) noexcept - { - return concat(to_array(left), right); - } - - template - constexpr auto concat(std::array const& left, T const right) noexcept - { - return concat(left, std::array{right}); - } - - template - constexpr auto concat(T const left, std::array const& right) noexcept - { - return concat(std::array{left}, right); - } - - template - constexpr auto combine(First const& first, Rest const&... rest) noexcept - { - if constexpr (sizeof...(rest) == 0) - { - return to_array(first); - } - else - { - return concat(first, combine(rest...)); - } - } - - constexpr std::array to_array(unsigned long value) noexcept - { - return { static_cast(value & 0x000000ff), static_cast((value & 0x0000ff00) >> 8), static_cast((value & 0x00ff0000) >> 16), static_cast((value & 0xff000000) >> 24) }; - } - - constexpr std::array to_array(unsigned short value) noexcept - { - return { static_cast(value & 0x00ff), static_cast((value & 0xff00) >> 8) }; - } - - constexpr auto to_array(GUID const& value) noexcept - { - return combine(to_array(value.Data1), to_array(value.Data2), to_array(value.Data3), - std::array{ value.Data4[0], value.Data4[1], value.Data4[2], value.Data4[3], value.Data4[4], value.Data4[5], value.Data4[6], value.Data4[7] }); - } - - auto calculate_sha1(std::vector const& input) - { - auto input_size = input.size(); - - std::array intermediate_hash{ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; - uint32_t i = 0; - while (i + 64 <= input_size) - { - intermediate_hash = process_msg_block(input.data(), i, intermediate_hash); - i += 64; - } - - auto length = size_to_bytes(input_size * 8); - auto remainder_size = (input_size % 64) + 1; - if (remainder_size + 8 <= 64) - { - std::array remainder{}; - std::copy(input.begin() + i, input.end(), remainder.begin()); - remainder[remainder_size - 1] = 0x80; - std::copy(length.begin(), length.end(), remainder.end() - 8); - intermediate_hash = process_msg_block(remainder.data(), 0, intermediate_hash); - } - else - { - std::array remainder{}; - std::copy(input.begin() + i, input.end(), remainder.begin()); - remainder[remainder_size - 1] = 0x80; - std::copy(length.begin(), length.end(), remainder.end() - 8); - intermediate_hash = process_msg_block(remainder.data(), 0, intermediate_hash); - intermediate_hash = process_msg_block(remainder.data(), 64, intermediate_hash); - } - - return get_result(intermediate_hash); - } - - GUID generate_guid(std::string const& sig) - { - constexpr GUID namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; - - constexpr auto namespace_bytes = to_array(namespace_guid); - - std::vector buffer{ namespace_bytes.begin(), namespace_bytes.end() }; - buffer.insert(buffer.end(), sig.begin(), sig.end()); - - return set_named_guid_fields(endian_swap(to_guid(calculate_sha1(buffer)))); - } -} \ No newline at end of file diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h deleted file mode 100644 index 2b45b02cf5..0000000000 --- a/src/cswinrt/helpers.h +++ /dev/null @@ -1,1838 +0,0 @@ -#pragma once - -namespace cswinrt -{ - using namespace std::literals; - using namespace winmd::reader; - - std::string get_mapped_element_type(ElementType elementType); - bool is_default_interface(InterfaceImpl const& ifaceImpl); - - static inline bool starts_with(std::string_view const& value, std::string_view const& match) noexcept - { - return 0 == value.compare(0, match.size(), match); - } - - // Converts a PascalCase name to camelCase (lowering the first character). - // Uses invariant ASCII lowering to avoid locale-dependent behavior. - // If the first character is not an uppercase ASCII letter, the name is - // returned unchanged (callers should use 'this.' to disambiguate). - static inline std::string to_camel_case(std::string_view const& name) - { - std::string result(name); - if (!result.empty() && result[0] >= 'A' && result[0] <= 'Z') - { - result[0] = result[0] - 'A' + 'a'; - } - return result; - } - - static bool is_remove_overload(MethodDef const& method) - { - return method.SpecialName() && starts_with(method.Name(), "remove_"); - } - - template - bool has_attribute(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) - { - return static_cast(get_attribute(row, type_namespace, type_name)); - } - - static bool is_noexcept(MethodDef const& method) - { - return is_remove_overload(method) || has_attribute(method, "Windows.Foundation.Metadata", "NoExceptionAttribute"); - } - - static bool is_noexcept(Property const& prop) - { - return has_attribute(prop, "Windows.Foundation.Metadata", "NoExceptionAttribute"); - } - - bool is_exclusive_to(TypeDef const& type) - { - return get_category(type) == category::interface_type && has_attribute(type, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); - } - - TypeDef get_exclusive_to_type(TypeDef const& type) - { - if (auto exclusive_to_attr = get_attribute(type, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) - { - auto sig = exclusive_to_attr.Value(); - auto const& fixed_args = sig.FixedArgs(); - XLANG_ASSERT(fixed_args.size() == 1); - auto sys_type = std::get(std::get(fixed_args[0].value).value); - return type.get_cache().find_required(sys_type.name); - } - - throw_invalid("Exclusive type not found"); - } - - bool is_overridable(InterfaceImpl const& interfaceImpl) - { - return has_attribute(interfaceImpl, "Windows.Foundation.Metadata"sv, "OverridableAttribute"sv); - } - - bool is_projection_internal(TypeDef const& type) - { - return has_attribute(type, "WindowsRuntime.Internal"sv, "ProjectionInternalAttribute"sv); - } - - bool is_flags_enum(TypeDef const& type) - { - return get_category(type) == category::enum_type && has_attribute(type, "System"sv, "FlagsAttribute"sv); - } - - bool is_api_contract_type(TypeDef const& type) - { - return get_category(type) == category::struct_type && has_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv); - } - - bool is_attribute_type(TypeDef const& type) - { - return get_category(type) == category::class_type && extends_type(type, "System"sv, "Attribute"sv); - } - - bool is_ptype(TypeDef const& type) - { - return distance(type.GenericParam()) > 0; - } - - bool is_static(TypeDef const& type) - { - return get_category(type) == category::class_type && type.Flags().Abstract(); - } - - bool is_constructor(MethodDef const& method) - { - return method.Flags().RTSpecialName() && method.Name() == ".ctor"; - } - - bool has_default_constructor(TypeDef const& type) - { - XLANG_ASSERT(get_category(type) == category::class_type); - - for (auto&& method : type.MethodList()) - { - if (is_constructor(method) && size(method.ParamList()) == 0) - { - return true; - } - } - - return false; - } - - bool is_special(MethodDef const& method) - { - return method.SpecialName() || method.Flags().RTSpecialName(); - } - - bool is_static(MethodDef const& method) - { - return method.Flags().Static(); - } - - auto get_delegate_invoke(TypeDef const& type) - { - XLANG_ASSERT(get_category(type) == category::delegate_type); - - for (auto&& method : type.MethodList()) - { - if (method.SpecialName() && (method.Name() == "Invoke")) - { - return method; - } - } - - throw_invalid("Invoke method not found"); - } - - bool is_projected_as_nullable(TypeDef const& type) - { - return type.TypeNamespace() == "Windows.Foundation" && type.TypeName() == "IReference`1"; - } - - enum class fundamental_type - { - Boolean, - Char, - Int8, - UInt8, - Int16, - UInt16, - Int32, - UInt32, - Int64, - UInt64, - Float, - Double, - String, - }; - - inline const char* to_string(fundamental_type ft) { - switch (ft) { - case fundamental_type::Boolean: return "Boolean"; - case fundamental_type::Char: return "Char"; - case fundamental_type::Int8: return "Byte"; - case fundamental_type::UInt8: return "Byte"; - case fundamental_type::Int16: return "Int16"; - case fundamental_type::UInt16: return "UInt16"; - case fundamental_type::Int32: return "Int32"; - case fundamental_type::UInt32: return "UInt32"; - case fundamental_type::Int64: return "Int64"; - case fundamental_type::UInt64: return "UInt64"; - case fundamental_type::Float: return "Float"; - case fundamental_type::Double: return "Double"; - case fundamental_type::String: return "String"; - default: return "Unknown"; - } - } - - struct generic_type_instance; - struct object_type {}; - struct guid_type {}; - struct type_type {}; - using type_definition = TypeDef; - using generic_type_index = GenericTypeIndex; - using generic_type_param = GenericParam; - - using type_semantics = std::variant< - fundamental_type, - object_type, - guid_type, - type_type, - type_definition, - generic_type_instance, - generic_type_index, - generic_type_param>; - - struct generic_type_instance - { - type_definition generic_type; - std::vector generic_args{}; - }; - - type_semantics get_type_semantics(TypeSig const& signature); - - type_semantics get_type_semantics(GenericTypeInstSig const& type) - { - auto generic_type_helper = [&type]() - { - switch (type.GenericType().type()) - { - case TypeDefOrRef::TypeDef: - return type.GenericType().TypeDef(); - case TypeDefOrRef::TypeRef: - return find_required(type.GenericType().TypeRef()); - } - - throw_invalid("invalid TypeDefOrRef value for GenericTypeInstSig.GenericType"); - }; - - auto gti = generic_type_instance{ generic_type_helper() }; - - for (auto&& arg : type.GenericArgs()) - { - gti.generic_args.push_back(get_type_semantics(arg)); - } - - return gti; - } - - type_semantics get_type_semantics(coded_index const& type) - { - switch (type.type()) - { - case TypeDefOrRef::TypeDef: - return type.TypeDef(); - case TypeDefOrRef::TypeRef: - { - auto type_ref = type.TypeRef(); - if (type_ref.TypeName() == "Guid" && type_ref.TypeNamespace() == "System") - { - return guid_type{}; - } - - if (type_ref.TypeName() == "Object" && type_ref.TypeNamespace() == "System") - { - return object_type{}; - } - - if (type_ref.TypeName() == "Type" && type_ref.TypeNamespace() == "System") - { - return type_type{}; - } - - return find_required(type_ref); - } - case TypeDefOrRef::TypeSpec: - return get_type_semantics(type.TypeSpec().Signature().GenericTypeInst()); - } - - throw_invalid("TypeDefOrRef not supported"); - } - - namespace impl - { - template struct overloaded : Ts... { using Ts::operator()...; }; - template overloaded(Ts...)->overloaded; - } - - type_semantics get_type_semantics(TypeSig const& signature) - { - return std::visit( - impl::overloaded{ - [](ElementType type) -> type_semantics - { - switch (type) - { - case ElementType::Boolean: - return fundamental_type::Boolean; - case ElementType::Char: - return fundamental_type::Char; - case ElementType::I1: - return fundamental_type::Int8; - case ElementType::U1: - return fundamental_type::UInt8; - case ElementType::I2: - return fundamental_type::Int16; - case ElementType::U2: - return fundamental_type::UInt16; - case ElementType::I4: - return fundamental_type::Int32; - case ElementType::U4: - return fundamental_type::UInt32; - case ElementType::I8: - return fundamental_type::Int64; - case ElementType::U8: - return fundamental_type::UInt64; - case ElementType::R4: - return fundamental_type::Float; - case ElementType::R8: - return fundamental_type::Double; - case ElementType::String: - return fundamental_type::String; - case ElementType::Object: - return object_type{}; - } - throw_invalid("element type not supported: " + get_mapped_element_type(type)); - }, - [](coded_index type) -> type_semantics - { - return get_type_semantics(type); - }, - [](GenericTypeIndex var) -> type_semantics { - return generic_type_index{ var.index }; }, - [](GenericTypeInstSig sig) -> type_semantics { - return get_type_semantics(sig); }, - [](GenericMethodTypeIndex) -> type_semantics { throw_invalid("Generic methods not supported"); } - }, signature.Type()); - } - - struct method_signature - { - using param_t = std::pair; - - explicit method_signature(MethodDef const& method) : - m_method(method.Signature()) - { - auto params = method.ParamList(); - - if (m_method.ReturnType() && params.first != params.second && params.first.Sequence() == 0) - { - m_return = params.first; - ++params.first; - } - - for (uint32_t i{}; i != size(m_method.Params()); ++i) - { - m_params.emplace_back(params.first + i, &m_method.Params().first[i]); - } - } - - std::vector& params() - { - return m_params; - } - - std::vector const& params() const - { - return m_params; - } - - auto const& return_signature() const - { - return m_method.ReturnType(); - } - - auto return_param_name(std::string_view default_name = "__return_value__") const - { - if (m_return) - { - return m_return.Name(); - } - else - { - return default_name; - } - } - - bool has_params() const - { - return !m_params.empty(); - } - - private: - - MethodDefSig m_method; - std::vector m_params; - Param m_return; - }; - - enum class param_category - { - in, - ref, - out, - pass_array, - fill_array, - receive_array, - }; - - auto get_param_category(method_signature::param_t const& param) - { - if (param.second->Type().is_szarray()) - { - if (param.first.Flags().In()) - { - return param_category::pass_array; - } - else if (param.second->ByRef()) - { - XLANG_ASSERT(param.first.Flags().Out()); - return param_category::receive_array; - } - else - { - XLANG_ASSERT(param.first.Flags().Out()); - return param_category::fill_array; - } - } - else - { - if(param.first.Flags().Out()) - { - return param_category::out; - } - else if (param.second->ByRef()) - { - return param_category::ref; - } - else - { - return param_category::in; - } - } - } - - auto get_property_methods(Property const& prop) - { - MethodDef get_method{}, set_method{}; - - for (auto&& method_semantic : prop.MethodSemantic()) - { - auto semantic = method_semantic.Semantic(); - - if (semantic.Getter()) - { - get_method = method_semantic.Method(); - } - else if (semantic.Setter()) - { - set_method = method_semantic.Method(); - } - else - { - throw_invalid("Properties can only have get and set methods"); - } - } - - XLANG_ASSERT(get_method || set_method); - - if (get_method && set_method) - { - XLANG_ASSERT(get_method.Flags().Static() == set_method.Flags().Static()); - } - - return std::make_tuple(get_method, set_method); - } - - auto get_event_methods(Event const& evt) - { - MethodDef add_method{}, remove_method{}; - - for (auto&& method_semantic : evt.MethodSemantic()) - { - auto semantic = method_semantic.Semantic(); - - if (semantic.AddOn()) - { - add_method = method_semantic.Method(); - } - else if (semantic.RemoveOn()) - { - remove_method = method_semantic.Method(); - } - else - { - throw_invalid("Events can only have add and remove methods"); - } - } - - XLANG_ASSERT(add_method); - XLANG_ASSERT(remove_method); - XLANG_ASSERT(add_method.Flags().Static() == remove_method.Flags().Static()); - - return std::make_tuple(add_method, remove_method); - } - - inline coded_index get_default_interface(TypeDef const& type) - { - auto impls = type.InterfaceImpl(); - - for (auto&& impl : impls) - { - if (is_default_interface(impl)) - { - return impl.Interface(); - } - } - - if (!empty(impls)) - { - throw_invalid("Type '", type.TypeNamespace(), ".", type.TypeName(), "' does not have a default interface"); - } - - return {}; - } - - template - bool is_param_expected_type(TypeSig const& param, T const& expected_type) - { - auto semantics = get_type_semantics(param); - if (auto variant_type = std::get_if(&semantics)) - { - return *variant_type == expected_type; - } - - return false; - } - - // Checks for expected method name and parameters and returns true if those - // match (ignoring the return type). The parameter return_type_matches indicates whether - // the return type also matches. - template - bool is_expected_method( - MethodDef const& method, - std::string_view expected_method, - ParamMatches&& check_expected_params, - ReturnMatches&& check_expected_return, - bool* return_type_matches = nullptr) - { - if (method.Name() != expected_method) - { - return false; - } - - method_signature signature(method); - if (!check_expected_params(signature)) - { - return false; - } - - if (return_type_matches) - { - *return_type_matches = false; - if (check_expected_return(signature)) - { - *return_type_matches = true; - } - } - - return true; - } - - // Checks for Equals(object obj). - bool is_object_equals_method(MethodDef const& method, bool* return_type_matches = nullptr) - { - auto checkParams = [](method_signature signature) - { - return signature.has_params() && - signature.params().size() == 1 && - std::holds_alternative(get_type_semantics(signature.params()[0].second->Type())); - }; - - auto checkReturn = [](method_signature signature) - { - if (auto return_sig = signature.return_signature()) - { - if (is_param_expected_type(return_sig.Type(), fundamental_type::Boolean)) - { - return true; - } - } - - return false; - }; - - return is_expected_method(method, "Equals"sv, checkParams, checkReturn, return_type_matches); - } - - // Checks for Equals(Class obj). - bool is_class_equals_method(MethodDef const& method, std::string_view expected_namespace, std::string_view expected_type_name, bool* return_type_matches = nullptr) - { - auto checkParams = [&expected_namespace, &expected_type_name](method_signature signature) - { - if (signature.has_params() && signature.params().size() == 1) - { - auto semantics = get_type_semantics(signature.params()[0].second->Type()); - if (auto td = std::get_if(&semantics)) - { - return td->TypeNamespace() == expected_namespace && td->TypeName() == expected_type_name; - } - } - - return false; - }; - - auto checkReturn = [](method_signature signature) - { - if (auto return_sig = signature.return_signature()) - { - if (is_param_expected_type(return_sig.Type(), fundamental_type::Boolean)) - { - return true; - } - } - - return false; - }; - - return is_expected_method(method, "Equals"sv, checkParams, checkReturn, return_type_matches); - } - - // Checks for GetHashCode(). - bool is_object_hashcode_method(MethodDef const& method, bool* return_type_matches = nullptr) - { - auto checkParams = [](method_signature signature) - { - return !signature.has_params(); - }; - - auto checkReturn = [](method_signature signature) - { - if (auto return_sig = signature.return_signature()) - { - if (is_param_expected_type(return_sig.Type(), fundamental_type::Int32)) - { - return true; - } - } - - return false; - }; - - return is_expected_method(method, "GetHashCode"sv, checkParams, checkReturn, return_type_matches); - } - - bool has_object_equals_method(TypeDef const& type, bool* return_type_matches = nullptr) - { - XLANG_ASSERT(get_category(type) == category::class_type); - - for (auto&& method : type.MethodList()) - { - if (is_object_equals_method(method, return_type_matches)) - { - return true; - } - } - - return false; - } - - bool has_class_equals_method(TypeDef const& type, bool* return_type_matches = nullptr) - { - XLANG_ASSERT(get_category(type) == category::class_type); - - for (auto&& method : type.MethodList()) - { - if (is_class_equals_method(method, type.TypeNamespace(), type.TypeName(), return_type_matches)) - { - return true; - } - } - - return false; - } - - bool has_object_hashcode_method(TypeDef const& type, bool* return_type_matches = nullptr) - { - XLANG_ASSERT(get_category(type) == category::class_type); - - for (auto&& method : type.MethodList()) - { - if (is_object_hashcode_method(method, return_type_matches)) - { - return true; - } - } - - return false; - } - - bool is_mapped_type_in_system_objectmodel(std::string_view typeNamespace, std::string_view typeName) - { - if (typeNamespace == "System.Collections.Specialized"sv) - { - if (typeName == "INotifyCollectionChanged"sv || - typeName == "NotifyCollectionChangedAction"sv || - typeName == "NotifyCollectionChangedEventArgs"sv || - typeName == "NotifyCollectionChangedEventHandler"sv) - { - return true; - } - } - else if (typeNamespace == "System.ComponentModel"sv) - { - if (typeName == "INotifyDataErrorInfo"sv || - typeName == "INotifyPropertyChanged"sv || - typeName == "DataErrorsChangedEventArgs"sv || - typeName == "PropertyChangedEventArgs"sv || - typeName == "PropertyChangedEventHandler"sv) - { - return true; - } - } - else if (typeNamespace == "System.Windows.Input"sv) - { - if (typeName == "ICommand"sv) - { - return true; - } - } - - return false; - } - - bool is_mapped_type_in_system_numerics_vectors(std::string_view typeNamespace) - { - return typeNamespace == "System.Numerics"sv; - } - - struct mapped_type - { - std::string_view abi_name; - std::string_view mapped_namespace; - std::string_view mapped_name; - bool requires_marshaling; - bool has_custom_members_output; - bool emit_abi; - }; - - inline const std::initializer_list get_mapped_types_in_namespace(std::string_view typeNamespace) - { - static const struct - { - std::string_view name_space; - std::initializer_list types; - } mapped_types[] = - { - // Make sure to keep this table consistent with the registrations in WinRT.Runtime/Projections.cs - // and the reverse mapping in WinRT.SourceGenerator/TypeMapper.cs. - // This table can include both the MUX and WUX types as only one will be selected at runtime. - // NOTE: Must keep namespaces sorted (outer) and abi type names sorted (inner) - { "Microsoft.UI.Xaml", - { - { "CornerRadius", "Microsoft.UI.Xaml", "CornerRadius", false, false, true }, - { "CornerRadiusHelper" }, - { "Duration", "Microsoft.UI.Xaml", "Duration", false, false, true }, - { "DurationHelper" }, - { "GridLength", "Microsoft.UI.Xaml", "GridLength", false, false, true }, - { "GridLengthHelper" }, - { "ICornerRadiusHelper" }, - { "ICornerRadiusHelperStatics" }, - { "IDurationHelper" }, - { "IDurationHelperStatics" }, - { "IGridLengthHelper" }, - { "IGridLengthHelperStatics" }, - { "IThicknessHelper" }, - { "IThicknessHelperStatics" }, - { "IXamlServiceProvider", "System", "IServiceProvider" }, - { "ThicknessHelper" }, - } - }, - { "Microsoft.UI.Xaml.Controls.Primitives", - { - { "GeneratorPositionHelper" }, - { "IGeneratorPositionHelper" }, - { "IGeneratorPositionHelperStatics" }, - } - }, - { "Microsoft.UI.Xaml.Data", - { - { "DataErrorsChangedEventArgs", "System.ComponentModel", "DataErrorsChangedEventArgs" }, - { "INotifyDataErrorInfo", "System.ComponentModel", "INotifyDataErrorInfo", true, true }, - { "INotifyPropertyChanged", "System.ComponentModel", "INotifyPropertyChanged" }, - { "PropertyChangedEventArgs", "System.ComponentModel", "PropertyChangedEventArgs" }, - { "PropertyChangedEventHandler", "System.ComponentModel", "PropertyChangedEventHandler" }, - } - }, - { "Microsoft.UI.Xaml.Input", - { - { "ICommand", "System.Windows.Input", "ICommand", true }, - } - }, - { "Microsoft.UI.Xaml.Interop", - { - { "IBindableIterable", "System.Collections", "IEnumerable", true, true }, - { "IBindableIterator", "System.Collections", "IEnumerator", true, true }, - { "IBindableVector", "System.Collections", "IList", true, true }, - { "INotifyCollectionChanged", "System.Collections.Specialized", "INotifyCollectionChanged", true }, - { "NotifyCollectionChangedAction", "System.Collections.Specialized", "NotifyCollectionChangedAction" }, - { "NotifyCollectionChangedEventArgs", "System.Collections.Specialized", "NotifyCollectionChangedEventArgs", true }, - { "NotifyCollectionChangedEventHandler", "System.Collections.Specialized", "NotifyCollectionChangedEventHandler", true }, - } - }, - { "Microsoft.UI.Xaml.Media", - { - { "IMatrixHelper" }, - { "IMatrixHelperStatics" }, - { "MatrixHelper" }, - } - }, - { "Microsoft.UI.Xaml.Media.Animation", - { - { "IKeyTimeHelper" }, - { "IKeyTimeHelperStatics" }, - { "IRepeatBehaviorHelper" }, - { "IRepeatBehaviorHelperStatics" }, - { "KeyTime", "Microsoft.UI.Xaml.Media.Animation", "KeyTime", false, false, true }, - { "KeyTimeHelper" }, - { "RepeatBehavior", "Microsoft.UI.Xaml.Media.Animation", "RepeatBehavior", false, false, true }, - { "RepeatBehaviorHelper" }, - } - }, - { "Microsoft.UI.Xaml.Media.Media3D", - { - { "IMatrix3DHelper" }, - { "IMatrix3DHelperStatics" }, - { "Matrix3D", "Microsoft.UI.Xaml.Media.Media3D", "Matrix3D", false, false, true }, - { "Matrix3DHelper" }, - } - }, - { "Windows.Foundation", - { - { "AsyncActionCompletedHandler", "Windows.Foundation", "AsyncActionCompletedHandler" }, - { "AsyncActionProgressHandler`1", "Windows.Foundation", "AsyncActionProgressHandler`1"}, - { "AsyncActionWithProgressCompletedHandler`1", "Windows.Foundation", "AsyncActionWithProgressCompletedHandler`1"}, - { "AsyncOperationCompletedHandler`1", "Windows.Foundation", "AsyncOperationCompletedHandler`1"}, - { "AsyncOperationProgressHandler`2", "Windows.Foundation", "AsyncOperationProgressHandler`2"}, - { "AsyncOperationWithProgressCompletedHandler`2", "Windows.Foundation", "AsyncOperationWithProgressCompletedHandler`2"}, - { "AsyncStatus", "Windows.Foundation", "AsyncStatus" }, - { "DateTime", "System", "DateTimeOffset", true }, - { "EventHandler`1", "System", "EventHandler`1", false }, - { "EventRegistrationToken", "WindowsRuntime.InteropServices", "EventRegistrationToken", false }, - { "FoundationContract", "Windows.Foundation", "FoundationContract"}, - { "HResult", "System", "Exception", true }, - { "IAsyncAction", "Windows.Foundation", "IAsyncAction" }, - { "IAsyncActionWithProgress`1", "Windows.Foundation", "IAsyncActionWithProgress`1" }, - { "IAsyncInfo", "Windows.Foundation", "IAsyncInfo" }, - { "IAsyncOperationWithProgress`2", "Windows.Foundation", "IAsyncOperationWithProgress`2" }, - { "IAsyncOperation`1", "Windows.Foundation", "IAsyncOperation`1" }, - { "IClosable", "System", "IDisposable", true, true }, - { "IMemoryBufferReference", "Windows.Foundation", "IMemoryBufferReference" }, - { "IPropertyValue", "Windows.Foundation", "IPropertyValue", true }, - { "IReferenceArray`1", "Windows.Foundation", "IReferenceArray", true }, - { "IReference`1", "System", "Nullable`1", true }, - { "IStringable", "Windows.Foundation", "IStringable" }, - { "Point", "Windows.Foundation", "Point" }, - { "PropertyType", "Windows.Foundation", "PropertyType" }, - { "Rect", "Windows.Foundation", "Rect" }, - { "Size", "Windows.Foundation", "Size" }, - { "TimeSpan", "System", "TimeSpan", true }, - { "TypedEventHandler`2", "System", "EventHandler`2", false }, - { "UniversalApiContract", "Windows.Foundation", "UniversalApiContract"}, - { "Uri", "System", "Uri", true }, - } - }, - { "Windows.Foundation.Collections", - { - { "CollectionChange", "Windows.Foundation.Collections", "CollectionChange" }, - { "IIterable`1", "System.Collections.Generic", "IEnumerable`1", true, true }, - { "IIterator`1", "System.Collections.Generic", "IEnumerator`1", true, true }, - { "IKeyValuePair`2", "System.Collections.Generic", "KeyValuePair`2", true }, - { "IMapChangedEventArgs`1", "Windows.Foundation.Collections", "IMapChangedEventArgs`1" }, - { "IMapView`2", "System.Collections.Generic", "IReadOnlyDictionary`2", true, true }, - { "IMap`2", "System.Collections.Generic", "IDictionary`2", true, true }, - { "IObservableMap`2", "Windows.Foundation.Collections", "IObservableMap`2" }, - { "IObservableVector`1", "Windows.Foundation.Collections", "IObservableVector`1" }, - { "IVectorChangedEventArgs", "Windows.Foundation.Collections", "IVectorChangedEventArgs" }, - { "IVectorView`1", "System.Collections.Generic", "IReadOnlyList`1", true, true }, - { "IVector`1", "System.Collections.Generic", "IList`1", true, true }, - { "MapChangedEventHandler`2", "Windows.Foundation.Collections", "MapChangedEventHandler`2" }, - { "VectorChangedEventHandler`1", "Windows.Foundation.Collections", "VectorChangedEventHandler`1" }, - } - }, - { "Windows.Foundation.Metadata", - { - { "ApiContractAttribute", "Windows.Foundation.Metadata", "ApiContractAttribute"}, - { "AttributeTargets", "System", "AttributeTargets" }, - { "AttributeUsageAttribute", "System", "AttributeUsageAttribute" }, - { "ContractVersionAttribute", "Windows.Foundation.Metadata", "ContractVersionAttribute"}, - } - }, - { "Windows.Foundation.Numerics", - { - { "Matrix3x2", "System.Numerics", "Matrix3x2" }, - { "Matrix4x4", "System.Numerics", "Matrix4x4" }, - { "Plane", "System.Numerics", "Plane" }, - { "Quaternion", "System.Numerics", "Quaternion" }, - { "Vector2", "System.Numerics", "Vector2" }, - { "Vector3", "System.Numerics", "Vector3" }, - { "Vector4", "System.Numerics", "Vector4" }, - } - }, - { "Windows.Storage.Streams", - { - { "IBuffer", "Windows.Storage.Streams", "IBuffer" }, - { "IInputStream", "Windows.Storage.Streams", "IInputStream" }, - { "IOutputStream", "Windows.Storage.Streams", "IOutputStream" }, - { "IRandomAccessStream", "Windows.Storage.Streams", "IRandomAccessStream" }, - { "InputStreamOptions", "Windows.Storage.Streams", "InputStreamOptions" }, - } - }, - { "Windows.UI.Xaml", - { - { "CornerRadius", "Windows.UI.Xaml", "CornerRadius", false, false, true }, - { "CornerRadiusHelper" }, - { "Duration", "Windows.UI.Xaml", "Duration", false, false, true }, - { "DurationHelper" }, - { "GridLength", "Windows.UI.Xaml", "GridLength", false, false, true }, - { "GridLengthHelper" }, - { "ICornerRadiusHelper" }, - { "ICornerRadiusHelperStatics" }, - { "IDurationHelper" }, - { "IDurationHelperStatics" }, - { "IGridLengthHelper" }, - { "IGridLengthHelperStatics" }, - { "IThicknessHelper" }, - { "IThicknessHelperStatics" }, - { "IXamlServiceProvider", "System", "IServiceProvider" }, - { "ThicknessHelper" }, - } - }, - { "Windows.UI.Xaml.Controls.Primitives", - { - { "GeneratorPositionHelper" }, - { "IGeneratorPositionHelper" }, - { "IGeneratorPositionHelperStatics" }, - } - }, - { "Windows.UI.Xaml.Data", - { - { "DataErrorsChangedEventArgs", "System.ComponentModel", "DataErrorsChangedEventArgs" }, - { "INotifyDataErrorInfo", "System.ComponentModel", "INotifyDataErrorInfo", true, true }, - { "INotifyPropertyChanged", "System.ComponentModel", "INotifyPropertyChanged" }, - { "PropertyChangedEventArgs", "System.ComponentModel", "PropertyChangedEventArgs" }, - { "PropertyChangedEventHandler", "System.ComponentModel", "PropertyChangedEventHandler" }, - } - }, - { "Windows.UI.Xaml.Input", - { - { "ICommand", "System.Windows.Input", "ICommand", true }, - } - }, - { "Windows.UI.Xaml.Interop", - { - { "IBindableIterable", "System.Collections", "IEnumerable", true, true }, - { "IBindableIterator", "System.Collections", "IEnumerator", true, true }, - { "IBindableVector", "System.Collections", "IList", true, true }, - { "INotifyCollectionChanged", "System.Collections.Specialized", "INotifyCollectionChanged", true }, - { "NotifyCollectionChangedAction", "System.Collections.Specialized", "NotifyCollectionChangedAction" }, - { "NotifyCollectionChangedEventArgs", "System.Collections.Specialized", "NotifyCollectionChangedEventArgs", true }, - { "NotifyCollectionChangedEventHandler", "System.Collections.Specialized", "NotifyCollectionChangedEventHandler", true }, - { "TypeKind", "Windows.UI.Xaml.Interop", "TypeKind", true }, - { "TypeName", "System", "Type", true }, - } - }, - { "Windows.UI.Xaml.Media", - { - { "IMatrixHelper" }, - { "IMatrixHelperStatics" }, - { "MatrixHelper" }, - } - }, - { "Windows.UI.Xaml.Media.Animation", - { - { "IKeyTimeHelper" }, - { "IKeyTimeHelperStatics" }, - { "IRepeatBehaviorHelper" }, - { "IRepeatBehaviorHelperStatics" }, - { "KeyTime", "Windows.UI.Xaml.Media.Animation", "KeyTime", false, false, true }, - { "KeyTimeHelper" }, - { "RepeatBehavior", "Windows.UI.Xaml.Media.Animation", "RepeatBehavior", false, false, true }, - { "RepeatBehaviorHelper" }, - } - }, - { "Windows.UI.Xaml.Media.Media3D", - { - { "IMatrix3DHelper" }, - { "IMatrix3DHelperStatics" }, - { "Matrix3D", "Windows.UI.Xaml.Media.Media3D", "Matrix3D", false, false, true }, - { "Matrix3DHelper" }, - } - }, - { "WindowsRuntime.Internal", - { - { "HWND", "System", "IntPtr" }, - { "ProjectionInternalAttribute" }, - } - }, - }; - - auto nsItr = std::lower_bound(std::begin(mapped_types), std::end(mapped_types), typeNamespace, [](auto&& v, std::string_view ns) - { - return v.name_space < ns; - }); - - if ((nsItr == std::end(mapped_types)) || (nsItr->name_space != typeNamespace)) - { - return {}; - } - - return nsItr->types; - } - - inline const mapped_type* get_mapped_type(std::string_view typeNamespace, std::string_view typeName) - { - auto mapped_types = get_mapped_types_in_namespace(typeNamespace); - - if (mapped_types.size() == 0) - { - return nullptr; - } - - auto nameItr = std::lower_bound(mapped_types.begin(), mapped_types.end(), typeName, [](auto&& v, std::string_view name) - { - return v.abi_name < name; - }); - if ((nameItr == mapped_types.end()) || (nameItr->abi_name != typeName)) - { - return nullptr; - } - - return &*nameItr; - } - - inline const std::string_view get_contract_platform(std::string_view contract_name, int32_t contract_version) - { - struct contract_platform - { - int32_t contract_version; - std::string_view platform_version; - }; - - static const struct - { - std::string_view contract_name; - std::vector versions; - } contract_mappings[] = - { - // Use PreviousPlatforms.linq LinqPad query to generate mapping data - { "Windows.AI.MachineLearning.MachineLearningContract", - { - { 1, "10.0.17763.0" }, - { 2, "10.0.18362.0" }, - { 3, "10.0.19041.0" }, - { 4, "10.0.20348.0" }, - { 5, "10.0.22000.0" }, - } - }, - { "Windows.AI.MachineLearning.Preview.MachineLearningPreviewContract", - { - { 1, "10.0.17134.0" }, - { 2, "10.0.17763.0" }, - } - }, - { "Windows.ApplicationModel.Calls.Background.CallsBackgroundContract", - { - { 1, "10.0.17763.0" }, - { 2, "10.0.18362.0" }, - { 3, "10.0.20348.0" }, - { 4, "10.0.22621.0" }, - } - }, - { "Windows.ApplicationModel.Calls.CallsPhoneContract", - { - { 4, "10.0.17763.0" }, - { 5, "10.0.18362.0" }, - { 6, "10.0.20348.0" }, - { 7, "10.0.22621.0" }, - } - }, - { "Windows.ApplicationModel.Calls.CallsVoipContract", - { - { 1, "10.0.10586.0" }, - { 2, "10.0.16299.0" }, - { 3, "10.0.17134.0" }, - { 4, "10.0.17763.0" }, - { 5, "10.0.26100.0" }, - } - }, - { "Windows.ApplicationModel.CommunicationBlocking.CommunicationBlockingContract", - { - { 2, "10.0.17763.0" }, - } - }, - { "Windows.ApplicationModel.SocialInfo.SocialInfoContract", - { - { 1, "10.0.14393.0" }, - { 2, "10.0.15063.0" }, - } - }, - { "Windows.ApplicationModel.StartupTaskContract", - { - { 2, "10.0.16299.0" }, - { 3, "10.0.17134.0" }, - } - }, - { "Windows.Devices.Custom.CustomDeviceContract", - { - { 1, "10.0.16299.0" }, - } - }, - { "Windows.Devices.DevicesLowLevelContract", - { - { 2, "10.0.14393.0" }, - { 3, "10.0.15063.0" }, - } - }, - { "Windows.Devices.Printers.PrintersContract", - { - { 1, "10.0.10586.0" }, - } - }, - { "Windows.Devices.SmartCards.SmartCardBackgroundTriggerContract", - { - { 3, "10.0.16299.0" }, - } - }, - { "Windows.Devices.SmartCards.SmartCardEmulatorContract", - { - { 5, "10.0.16299.0" }, - { 6, "10.0.17763.0" }, - } - }, - { "Windows.Foundation.FoundationContract", - { - { 1, "10.0.10240.0" }, - { 2, "10.0.10586.0" }, - { 3, "10.0.15063.0" }, - { 4, "10.0.19041.0" }, - } - }, - { "Windows.Foundation.UniversalApiContract", - { - { 1, "10.0.10240.0" }, - { 2, "10.0.10586.0" }, - { 3, "10.0.14393.0" }, - { 4, "10.0.15063.0" }, - { 5, "10.0.16299.0" }, - { 6, "10.0.17134.0" }, - { 7, "10.0.17763.0" }, - { 8, "10.0.18362.0" }, - { 10, "10.0.19041.0" }, - { 12, "10.0.20348.0" }, - { 14, "10.0.22000.0" }, - { 15, "10.0.22621.0" }, - { 19, "10.0.26100.0" }, - } - }, - { "Windows.Foundation.VelocityIntegration.VelocityIntegrationContract", - { - { 1, "10.0.17134.0" }, - } - }, - { "Windows.Gaming.XboxLive.StorageApiContract", - { - { 1, "10.0.16299.0" }, - } - }, - { "Windows.Graphics.Printing3D.Printing3DContract", - { - { 2, "10.0.10586.0" }, - { 3, "10.0.14393.0" }, - { 4, "10.0.16299.0" }, - } - }, - { "Windows.Networking.Connectivity.WwanContract", - { - { 1, "10.0.10240.0" }, - { 2, "10.0.17134.0" }, - { 3, "10.0.26100.0" }, - } - }, - { "Windows.Networking.Sockets.ControlChannelTriggerContract", - { - { 3, "10.0.17763.0" }, - } - }, - { "Windows.Security.Isolation.IsolatedWindowsEnvironmentContract", - { - { 1, "10.0.19041.0" }, - { 3, "10.0.20348.0" }, - { 4, "10.0.22621.0" }, - { 5, "10.0.26100.0" }, - } - }, - { "Windows.Services.Maps.GuidanceContract", - { - { 3, "10.0.17763.0" }, - } - }, - { "Windows.Services.Maps.LocalSearchContract", - { - { 4, "10.0.17763.0" }, - } - }, - { "Windows.Services.Store.StoreContract", - { - { 1, "10.0.14393.0" }, - { 2, "10.0.15063.0" }, - { 3, "10.0.17134.0" }, - { 4, "10.0.17763.0" }, - } - }, - { "Windows.Services.TargetedContent.TargetedContentContract", - { - { 1, "10.0.15063.0" }, - } - }, - { "Windows.Storage.Provider.CloudFilesContract", - { - { 4, "10.0.19041.0" }, - { 6, "10.0.20348.0" }, - { 7, "10.0.22621.0" }, - } - }, - { "Windows.System.Profile.ProfileHardwareTokenContract", - { - { 1, "10.0.14393.0" }, - } - }, - { "Windows.System.Profile.ProfileRetailInfoContract", - { - { 1, "10.0.20348.0" }, - } - }, - { "Windows.System.Profile.ProfileSharedModeContract", - { - { 1, "10.0.14393.0" }, - { 2, "10.0.15063.0" }, - } - }, - { "Windows.System.Profile.SystemManufacturers.SystemManufacturersContract", - { - { 3, "10.0.17763.0" }, - } - }, - { "Windows.System.SystemManagementContract", - { - { 6, "10.0.17763.0" }, - { 7, "10.0.19041.0" }, - } - }, - { "Windows.UI.UIAutomation.UIAutomationContract", - { - { 1, "10.0.20348.0" }, - { 2, "10.0.22000.0" }, - } - }, - { "Windows.UI.ViewManagement.ViewManagementViewScalingContract", - { - { 1, "10.0.14393.0" }, - } - }, - { "Windows.UI.Xaml.Core.Direct.XamlDirectContract", - { - { 1, "10.0.17763.0" }, - { 2, "10.0.18362.0" }, - { 3, "10.0.20348.0" }, - { 5, "10.0.22000.0" }, - } - }, - }; - - auto contractItr = std::lower_bound(std::begin(contract_mappings), std::end(contract_mappings), contract_name, [](auto&& c, std::string_view contract_name) - { - return c.contract_name < contract_name; - }); - - if ((contractItr == std::end(contract_mappings)) || (contractItr->contract_name != contract_name)) - { - return {}; - } - - auto& versions = contractItr->versions; - auto versionItr = std::lower_bound(std::begin(versions), std::end(versions), contract_version, [](auto&& v, int32_t contract_version) - { - return v.contract_version < contract_version; - }); - - if (versionItr == std::end(versions)) - { - return {}; - } - - return versionItr->platform_version; - } - - bool has_addition_to_type(TypeDef const& type) - { - static const struct - { - std::string_view name_space; - std::vector types; - } addition_types[] = - { - { "Microsoft.UI.Xaml", - { - "Thickness" - } - }, - { "Microsoft.UI.Xaml.Controls.Primitives", - { - "GeneratorPosition" - } - }, - { "Microsoft.UI.Xaml.Media", - { - "Matrix" - } - }, - { "Microsoft.UI.Xaml.Media.Animation", - { - "KeyTime" - } - }, - { "Windows.UI", - { - "Color", - } - }, - { "Windows.UI.Xaml", - { - "Thickness" - } - }, - { "Windows.UI.Xaml.Controls.Primitives", - { - "GeneratorPosition" - } - }, - { "Windows.UI.Xaml.Media", - { - "Matrix" - } - }, - { "Windows.UI.Xaml.Media.Animation", - { - "KeyTime" - } - }, - }; - - auto nsItr = std::lower_bound(std::begin(addition_types), std::end(addition_types), type.TypeNamespace(), [](auto&& v, std::string_view ns) - { - return v.name_space < ns; - }); - - if ((nsItr == std::end(addition_types)) || (nsItr->name_space != type.TypeNamespace())) - { - return false; - } - - auto nameItr = std::lower_bound(nsItr->types.begin(), nsItr->types.end(), type.TypeName(), [](auto&& v, std::string_view name) - { - return v < name; - }); - if ((nameItr == nsItr->types.end()) || (*nameItr != type.TypeName())) - { - return false; - } - - return true; - } - - enum class typedef_name_type - { - Projected, - CCW, - ABI, - NonProjected, - StaticAbiClass, - EventSource, - // Used only with interop dll name - Marshaller, - ArrayMarshaller, - InteropIID - }; - - std::string get_mapped_element_type(ElementType elementType) - { - switch (elementType) - { - case ElementType::End: - return "End"; - case ElementType::Void: - return "Void"; - case ElementType::Boolean: - return "Boolean"; - case ElementType::Char: - return "Char"; - case ElementType::I1: - return "I1"; - case ElementType::U1: - return "UI"; - case ElementType::I2: - return "I2"; - case ElementType::U2: - return "U2"; - case ElementType::I4: - return "I4"; - case ElementType::U4: - return "U4"; - case ElementType::I8: - return "I8"; - case ElementType::U8: - return "U8"; - case ElementType::R4: - return "R4"; - case ElementType::R8: - return "R8"; - case ElementType::String: - return "String"; - case ElementType::Ptr: - return "Ptr"; - case ElementType::ByRef: - return "ByRef"; - case ElementType::ValueType: - return "ValueType"; - case ElementType::Class: - return "Class"; - case ElementType::Var: - return "Var"; - case ElementType::Array: - return "Array"; - case ElementType::GenericInst: - return "GenericInst"; - case ElementType::TypedByRef: - return "TypedByRef"; - case ElementType::I: - return "IntPtr"; - case ElementType::U: - return "UIntPtr"; - case ElementType::FnPtr: - return "FnPtr"; - case ElementType::Object: - return "Object"; - case ElementType::SZArray: - return "SZArray"; - case ElementType::MVar: - return "MVar"; - case ElementType::CModReqd: - return "CModReqd"; - case ElementType::CModOpt: - return "CModOpt"; - case ElementType::Internal: - return "Internal"; - case ElementType::Modifier: - return "Modifier"; - case ElementType::Sentinel: - return "Sentinel"; - case ElementType::Pinned: - return "Pinned"; - case ElementType::Type: - return "Type"; - case ElementType::TaggedObject: - return "TaggedObject"; - case ElementType::Field: - return "Field"; - case ElementType::Property: - return "Property"; - case ElementType::Enum: - return "Enum"; - default: - return "Unknown"; - } - } - - template - int get_number_of_attributes(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) - { - auto count = 0; - for (auto&& attribute : row.CustomAttribute()) - { - auto pair = attribute.TypeNamespaceAndName(); - - if (pair.first == type_namespace && pair.second == type_name) - { - count++; - } - } - return count; - } - - int get_class_hierarchy_index(TypeDef const& classType) - { - auto sem = get_type_semantics(classType.Extends()); - if (std::holds_alternative(sem)) - { - return get_class_hierarchy_index(std::get(sem)) + 1; - } - return 0; - } - - bool interfaces_equal(TypeDef const& interface1, TypeDef const& interface2) - { - return interface1.TypeNamespace() == interface2.TypeNamespace() - && interface1.TypeName() == interface2.TypeName(); - } - - template - auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) - { - return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); - } - - std::optional get_contract_version(TypeDef const& type) - { - if (!has_attribute(type, "Windows.Foundation.Metadata", "ContractVersionAttribute")) - { - return {}; - } - return get_attribute_value(get_attribute(type, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv), 1); - } - - std::optional get_version(TypeDef const& type) - { - if (!has_attribute(type, "Windows.Foundation.Metadata", "VersionAttribute")) - { - return {}; - } - return get_attribute_value(get_attribute(type, "Windows.Foundation.Metadata"sv, "VersionAttribute"sv), 0); - } - - bool is_fast_abi_class(TypeDef const& type) - { - return has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv) && !settings.netstandard_compat; - } - - bool is_default_interface(InterfaceImpl const& ifaceImpl) - { - return has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute"); - } - - std::optional find_fast_abi_class_type(TypeDef const& iface) - { - static std::map> cache; - if (cache.find(iface) != cache.end()) - { - return cache[iface]; - } - auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); - if (exclusiveToAttribute) - { - auto sys_type = get_attribute_value(exclusiveToAttribute, 0); - TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); - if (!is_fast_abi_class(exclusiveToClass)) - { - return {}; - } - cache[iface] = exclusiveToClass; - return exclusiveToClass; - } - return {}; - } - - std::pair> get_default_and_exclusive_interfaces(TypeDef const& classType) - { - std::pair> exclusive_ifaces; - for (auto&& ifaceImpl : classType.InterfaceImpl()) - { - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (is_default_interface(ifaceImpl)) - { - exclusive_ifaces.first = std::get(sem); - } - else if (std::holds_alternative(sem)) - { - if (has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) - { - exclusive_ifaces.second.push_back(std::get(sem)); - } - } - } - return exclusive_ifaces; - } - - type_semantics get_default_iface_as_type_sem(TypeDef const& classType) - { - for (auto&& ifaceImpl : classType.InterfaceImpl()) - { - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (is_default_interface(ifaceImpl)) - { - return sem; - } - } - throw_invalid("Class does not have a default interface"); - } - - void sort_fast_abi_ifaces(std::vector& fast_abi_ifaces) - { - std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) - { - // compare relative contracts - auto relativeContractValueIface = -1 * get_number_of_attributes(iface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); - auto relativeContractValueOtherIface = -1 * get_number_of_attributes(otherIface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); - if (relativeContractValueIface != relativeContractValueOtherIface) - return relativeContractValueIface < relativeContractValueOtherIface; - - //compare contract versions if they exist - auto contractVersionIface = get_contract_version(iface); - auto contractVersionOtherIface = get_contract_version(otherIface); - if (contractVersionIface.has_value() && contractVersionOtherIface.has_value() && contractVersionIface.value() != contractVersionOtherIface.value()) - return contractVersionIface.value() < contractVersionOtherIface.value(); - - //compare versions - auto versionIface = get_version(iface); - auto versionOtherIface = get_version(otherIface); - if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) - return versionIface.value() < versionOtherIface.value(); - - //compare type names - return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); - }); - } - - struct fast_abi_class - { - const TypeDef class_type; - const TypeDef default_interface; - const std::vector other_interfaces; - - fast_abi_class(TypeDef class_type, TypeDef default_interface, std::vector other_interfaces) - : class_type(class_type), default_interface(default_interface), other_interfaces(other_interfaces) - { - int vtable_start_index = 6; - add_property_caches(default_interface, vtable_start_index); - vtable_start_index += distance(default_interface.MethodList()) + get_class_hierarchy_index(class_type); - for (auto&& other_iface : other_interfaces) - { - other_interfaces_cache.insert(std::pair(other_iface.TypeNamespace(), other_iface.TypeName())); - add_property_caches(other_iface, vtable_start_index); - vtable_start_index += distance(other_iface.MethodList()); - } - } - - bool contains_other_interface(TypeDef const& iface) - { - return other_interfaces_cache.find(std::pair(iface.TypeNamespace(), iface.TypeName())) != other_interfaces_cache.end(); - } - - std::pair find_property_setter(std::string_view property_name) - { - return property_setters_cache[property_name]; - } - - bool contains_setter(std::string_view property_name) - { - return property_setters_cache.find(property_name) != property_setters_cache.end(); - } - - bool contains_getter(std::string_view property_name) - { - return property_getters_cache.find(property_name) != property_getters_cache.end(); - } - - private: - std::set> other_interfaces_cache; - std::map> property_setters_cache; - std::set property_getters_cache; - - void add_property_caches(TypeDef const& iface, int vtable_start_index) - { - for (auto prop : iface.PropertyList()) - { - auto&& [getter, setter] = get_property_methods(prop); - if (getter) - { - property_getters_cache.insert(prop.Name()); - } - if (setter) - { - property_setters_cache[prop.Name()] = std::pair(setter, vtable_start_index); - } - } - } - }; - - std::optional get_fast_abi_class_for_class(TypeDef const& classType) - { - if (!is_fast_abi_class(classType)) - { - return {}; - } - auto [default_iface, other_ifaces] = get_default_and_exclusive_interfaces(classType); - sort_fast_abi_ifaces(other_ifaces); - return fast_abi_class(classType, default_iface, other_ifaces); - } - - std::optional get_fast_abi_class_for_interface(TypeDef const& iface) - { - auto fast_abi_class_type = find_fast_abi_class_type(iface); - if (!fast_abi_class_type.has_value()) { - return {}; - } - return get_fast_abi_class_for_class(fast_abi_class_type.value()); - } - - int get_gc_pressure_amount(TypeDef const& classType) - { - auto gc_pressure_amount = 0; - if (auto gc_pressure_attr = get_attribute(classType, "Windows.Foundation.Metadata", "GCPressureAttribute")) - { - // Restricting to sealed scenarios because unsealed scenarios require more handling to prevent mismatches in - // adding and removing memory pressure and none of the Windows namespace types which use it today are unsealed. - if (classType.Flags().Sealed()) - { - auto sig = gc_pressure_attr.Value(); - auto const& args = sig.NamedArgs(); - auto amount = std::get(std::get(std::get(args[0].value.value).value).value); - gc_pressure_amount = amount == 0 ? 12000 : amount == 1 ? 120000 : 1200000; - } - } - return gc_pressure_amount; - } - - std::string_view get_marshaling_type_name(TypeDef const& classType) - { - if (auto marshaling_attr = get_attribute(classType, "Windows.Foundation.Metadata"sv, "MarshalingBehaviorAttribute"sv)) - { - auto sig = marshaling_attr.Value(); - auto const& fixed_args = sig.FixedArgs(); - XLANG_ASSERT(fixed_args.size() == 1); - - auto marshaling_type = std::get(std::get(std::get(fixed_args[0].value).value).value); - - switch (marshaling_type) - { - case 2: return "CreateObjectReferenceMarshalingType.Agile"sv; - case 3: return "CreateObjectReferenceMarshalingType.Standard"sv; - } - } - - return "CreateObjectReferenceMarshalingType.Unknown"sv; - } - - struct generic_abi_delegate - { - std::string abi_delegate_name; - std::string abi_delegate_declaration; - std::string abi_delegate_types; - - // Hash / equality for the hast set this is added to is based on the types in the delegate - // as we do not need duplicate delegate entries from different collection types. - bool operator==(const generic_abi_delegate& entry) const - { - return abi_delegate_types == entry.abi_delegate_types; - } - }; - - struct generic_type_instantiation - { - generic_type_instance instance; - std::string instantiation_class_name; - - // Hash / equality for the hash set. - bool operator==(const generic_type_instantiation& other) const - { - return instantiation_class_name == other.instantiation_class_name; - } - }; - - std::string escape_type_name_for_identifier(std::string typeName, bool stripGlobal = false, bool stripGlobalABI = false) - { - std::regex re(R"-((\ |:|<|>|`|,|\.))-"); - auto result = std::regex_replace(typeName, re, "_"); - if (stripGlobalABI && typeName.rfind("global::ABI.", 0) != std::string::npos) - { - result.erase(0, 12); // Remove "global::" - } - else if (stripGlobal && typeName.rfind("global::", 0) != std::string::npos) - { - result.erase(0, 8); // Remove "global::" - } - return result; - } - - std::string get_fundamental_type_guid_signature(fundamental_type type) - { - switch (type) - { - case fundamental_type::Boolean: return "b1"; - case fundamental_type::Char: return "c2"; - case fundamental_type::Int8: return "i1"; - case fundamental_type::UInt8: return "u1"; - case fundamental_type::Int16: return "i2"; - case fundamental_type::UInt16: return "u2"; - case fundamental_type::Int32: return "i4"; - case fundamental_type::UInt32: return "u4"; - case fundamental_type::Int64: return "i8"; - case fundamental_type::UInt64: return "u8"; - case fundamental_type::Float: return "f4"; - case fundamental_type::Double: return "f8"; - case fundamental_type::String: return "string"; - default: throw_invalid("Unknown type"); - } - } - - // Returns whether the ABI interface should implement the CCW interface or not. - // In authoring scenarios, exclusive interfaces don't exist, so we use the CCW impl type. - bool does_abi_interface_implement_ccw_interface(TypeDef const& type) - { - return settings.component && - settings.filter.includes(type) && - is_exclusive_to(type); - } -} - -namespace std -{ - template<> - struct hash { - size_t operator()(const cswinrt::generic_abi_delegate& entry) const - { - return hash()(entry.abi_delegate_types); - } - }; - - template<> - struct hash { - size_t operator()(const cswinrt::generic_type_instantiation& instantiation) const - { - return hash()(instantiation.instantiation_class_name); - } - }; -} diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp deleted file mode 100644 index c91dae3119..0000000000 --- a/src/cswinrt/main.cpp +++ /dev/null @@ -1,527 +0,0 @@ -#include "pch.h" -#include "settings.h" -#include "helpers.h" -#include "type_writers.h" -#include "code_writers.h" -#include -#include -#include - -namespace cswinrt -{ - using namespace std::literals; - using namespace std::experimental::filesystem; - using namespace winmd::reader; - - inline auto get_start_time() - { - return std::chrono::high_resolution_clock::now(); - } - - inline auto get_elapsed_time(std::chrono::time_point const& start) - { - return std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start).count(); - } - - settings_type settings; - - struct usage_exception {}; - - static constexpr option options[] - { - { "input", 0, option::no_max, "", "Windows metadata to include in projection" }, - { "output", 0, 1, "", "Location of generated projection" }, - { "include", 0, option::no_max, "", "One or more prefixes to include in projection" }, - { "exclude", 0, option::no_max, "", "One or more prefixes to exclude from projection" }, - { "addition_exclude", 0, option::no_max, "", "One or more namespace prefixes to exclude from the projection additions" }, - { "target", 0, 1, "", "Target TFM for projection (.NET 10 is the default)" }, - { "component", 0, 0, {}, "Generate component projection." }, - { "verbose", 0, 0, {}, "Show detailed progress information" }, - { "internal", 0, 0, {}, "Generates a private projection."}, - { "embedded", 0, 0, {}, "Generates an embedded projection."}, - { "public_enums", 0, 0, {}, "Used with embedded option to generate enums as public"}, - { "public_exclusiveto", 0, 0, {}, "Make exclusiveto interfaces public in the projection (default is internal)"}, - { "idic_exclusiveto", 0, 0, {}, "Make exclusiveto interfaces support IDynamicInterfaceCastable (IDIC) for RCW scenarios (default is false)"}, - { "reference_projection", 0, 0, {}, "Generates a projection to be used as a reference assembly (default is false)"}, - { "help", 0, option::no_max, {}, "Show detailed help" }, - { "?", 0, option::no_max, {}, {} }, - }; - - static void print_usage(writer& w) - { - static auto printColumns = [](writer& w, std::string_view const& col1, std::string_view const& col2) - { - w.write_printf(" %-35s%s\n", col1.data(), col2.data()); - }; - - static auto printOption = [](writer& w, option const& opt) - { - if(opt.desc.empty()) - { - return; - } - printColumns(w, w.write_temp("-% %", opt.name, opt.arg), opt.desc); - }; - - auto format = R"( -C#/WinRT v% -Copyright (c) Microsoft Corporation. All rights reserved. - - cswinrt.exe [options...] - -Options: - -% ^@ Response file containing command line options - -Where is one or more of: - - path Path to winmd file or recursively scanned folder - local Local ^%WinDir^%\System32\WinMetadata folder - sdk[+] Current version of Windows SDK [with extensions] - 10.0.12345.0[+] Specific version of Windows SDK [with extensions] -)"; - w.write(format, VERSION_STRING, bind_each(printOption, options)); - } - - void process_args(int const argc, char** argv) - { - reader args{ argc, argv, options }; - - if (!args || args.exists("help") || args.exists("?")) - { - throw usage_exception{}; - } - - settings.verbose = args.exists("verbose"); - auto target = args.value("target"); - if (!target.empty() && !starts_with(target, "net10.0")) - { - throw usage_exception(); - } - else if (target.empty()) - { - // Default to .NET 10 if no explicit target is set - target = "net10.0"; - } - settings.netstandard_compat = false; - settings.component = args.exists("component"); - settings.internal = args.exists("internal"); - settings.embedded = args.exists("embedded"); - settings.public_enums = args.exists("public_enums"); - settings.public_exclusiveto = args.exists("public_exclusiveto"); - settings.idic_exclusiveto = args.exists("idic_exclusiveto"); - settings.reference_projection = args.exists("reference_projection"); - settings.input = args.files("input", database::is_database); - - for (auto && include : args.values("include")) - { - settings.include.insert(include); - } - - for (auto && exclude : args.values("exclude")) - { - settings.exclude.insert(exclude); - } - - for (auto&& addition_exclude : args.values("addition_exclude")) - { - settings.addition_exclude.insert(addition_exclude); - } - - settings.output_folder = std::filesystem::absolute(args.value("output", "output")); - create_directories(settings.output_folder); - } - - auto get_files_to_cache() - { - std::vector files; - files.insert(files.end(), settings.input.begin(), settings.input.end()); - return files; - } - - int run(int const argc, char** argv) - { - int result{}; - writer w; - - /* Special case the usage exceptions to print CLI options */ - try - { - auto start = get_start_time(); - process_args(argc, argv); - cache c{ get_files_to_cache() }; - settings.filter = { settings.include, settings.exclude }; - - // Include all additions for included namespaces by default - settings.addition_filter = { settings.include, settings.addition_exclude }; - - std::set componentActivatableClasses; - std::map> componentActivatableClassesByModule; - if (settings.component) - { - for (auto&& [ns, members] : c.namespaces()) - { - for (auto&& type : members.classes) - { - if (!settings.filter.includes(type)) { continue; } - for (auto&& attribute : type.CustomAttribute()) - { - auto attribute_name = attribute.TypeNamespaceAndName(); - if (attribute_name.first != "Windows.Foundation.Metadata") - { - continue; - } - - if (attribute_name.second == "ActivatableAttribute" || attribute_name.second == "StaticAttribute") - { - componentActivatableClasses.insert(type); - std::filesystem::path db_path(type.get_database().path()); - auto module_name = db_path.stem().string(); - componentActivatableClassesByModule[module_name].insert(type); - } - } - } - } - } - - if (settings.verbose) - { - for (auto&& file : settings.input) - { - w.write("input: %\n", file); - } - - w.write("output: %\n", settings.output_folder.string()); - } - - w.flush_to_console(); - - // Write GUID properties out to InterfaceIIDs static class - if (!settings.reference_projection) - { - bool iid_written = false; - std::set interfacesFromClassesEmitted; - writer guidWriter("ABI"); - guidWriter.write_begin_interface_iids(); - for (auto&& ns_members : c.namespaces()) - { - auto&& [ns, members] = ns_members; - for (auto&& [name, type] : members.types) - { - if (!settings.filter.includes(type)) { continue; } - if (distance(type.GenericParam()) != 0) { continue; } - if (auto mapping = get_mapped_type(ns, name)) - { - if (!mapping->emit_abi) - { - continue; - } - } - - iid_written = true; - - switch (get_category(type)) - { - case category::delegate_type: - write_iid_guid_property_from_signature(guidWriter, type); - write_iid_guid_property_from_type(guidWriter, type); - break; - case category::enum_type: - write_iid_guid_property_from_signature(guidWriter, type); - break; - case category::interface_type: - write_iid_guid_property_from_type(guidWriter, type); - break; - case category::struct_type: - write_iid_guid_property_from_signature(guidWriter, type); - break; - case category::class_type: - write_iid_guid_property_for_class_interfaces(guidWriter, type, interfacesFromClassesEmitted); - break; - } - } - } - guidWriter.write_end_interface_iids(); - if (iid_written) - { - auto filename = guidWriter.write_temp("%.cs", "GeneratedInterfaceIIDs"); - guidWriter.flush_to_file(settings.output_folder / filename); - } - } - - task_group group; - concurrency::concurrent_unordered_map authoredTypeNameToMetadataTypeNameMap; - concurrency::concurrent_unordered_map defaultInterfaceEntries; - concurrency::concurrent_vector> exclusiveToInterfaceEntries; - concurrency::concurrent_unordered_set abiDelegateEntries; - bool projectionFileWritten = false; - for (auto&& ns_members : c.namespaces()) - { - group.add([&ns_members, &componentActivatableClasses, &projectionFileWritten, &abiDelegateEntries, &authoredTypeNameToMetadataTypeNameMap, &defaultInterfaceEntries, &exclusiveToInterfaceEntries] - { - auto&& [ns, members] = ns_members; - std::string_view currentType = ""; - try - { - writer w(ns); - writer helperWriter("WinRT"); - w.write_begin(); - - if (!settings.reference_projection) - { - write_pragma_disable_IL2026(w); - - for (auto&& [name, type] : members.types) - { - currentType = name; - if (!settings.filter.includes(type)) { continue; } - if (distance(type.GenericParam()) != 0) { continue; } - if (auto mapping = get_mapped_type(ns, name)) - { - if (!mapping->emit_abi) - { - continue; - } - } - auto guard{ w.push_generic_params(type.GenericParam()) }; - auto guard1{ helperWriter.push_generic_params(type.GenericParam()) }; - - switch (get_category(type)) - { - case category::class_type: - // For both static and attributes, we don't need to pass them across the ABI. - if (!is_static(type) && !is_attribute_type(type)) - { - // For component types, they would be instantiated in C#, so we don't need - // the ComWrappers attribute. But we do need the metadata attribute to enable - // xaml type marshaling. - if (settings.component) - { - write_winrt_windowsmetadata_typemapgroup_assembly_attribute(w, type); - } - else - { - write_winrt_comwrappers_typemapgroup_assembly_attribute(w, type, false); - } - } - - break; - case category::delegate_type: - write_winrt_comwrappers_typemapgroup_assembly_attribute(w, type, true); - write_winrt_windowsmetadata_typemapgroup_assembly_attribute(w, type); - break; - case category::enum_type: - write_winrt_comwrappers_typemapgroup_assembly_attribute(w, type, true); - write_winrt_windowsmetadata_typemapgroup_assembly_attribute(w, type); - break; - case category::interface_type: - write_winrt_idic_typemapgroup_assembly_attribute(w, type); - write_winrt_windowsmetadata_typemapgroup_assembly_attribute(w, type); - break; - case category::struct_type: - // Similarly for API contracts, we don't expect them to be passed across the ABI. - if (!is_api_contract_type(type)) - { - write_winrt_comwrappers_typemapgroup_assembly_attribute(w, type, true); - write_winrt_windowsmetadata_typemapgroup_assembly_attribute(w, type); - } - break; - } - } - - write_pragma_restore_IL2026(w); - } - - currentType = ""; - - w.write_begin_projected(); - bool written = false; - for (auto&& [name, type] : members.types) - { - currentType = name; - if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) - { - written = true; - continue; - } - auto guard{ w.push_generic_params(type.GenericParam()) }; - auto guard1{ helperWriter.push_generic_params(type.GenericParam()) }; - - switch (get_category(type)) - { - case category::class_type: - if (is_attribute_type(type)) - { - write_attribute(w, type); - } - else - { - write_class(w, type); - add_default_interface_entry(w, type, defaultInterfaceEntries); - add_exclusive_to_interface_entries(w, type, exclusiveToInterfaceEntries); - add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); - if (settings.component && componentActivatableClasses.count(type) == 1) - { - write_factory_class(w, type); - } - } - break; - case category::delegate_type: - write_delegate(w, type); - add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); - break; - case category::enum_type: - write_enum(w, type); - add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); - break; - case category::interface_type: - write_interface(w, type); - add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); - break; - case category::struct_type: - if (is_api_contract_type(type)) - { - write_contract(w, type); - } - else - { - write_struct(w, type); - add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); - } - break; - } - - add_generic_type_references_in_type(type, abiDelegateEntries); - written = true; - } - currentType = ""; - if (written) - { - w.write_end_projected(); - - if (!settings.reference_projection) - { - w.write_begin_abi(); - - for (auto&& [name, type] : members.types) - { - currentType = name; - if (!settings.filter.includes(type)) { continue; } - if (distance(type.GenericParam()) != 0) { continue; } - if (auto mapping = get_mapped_type(ns, name)) - { - if (!mapping->emit_abi) - { - continue; - } - } - - if (is_api_contract_type(type)) { continue; } - if (is_attribute_type(type)) { continue; } - auto guard{ w.push_generic_params(type.GenericParam()) }; - - switch (get_category(type)) - { - case category::class_type: - write_abi_class(w, type); - break; - case category::delegate_type: - write_abi_delegate(w, type); - write_temp_delegate_event_source_subclass(w, type); - break; - case category::enum_type: - write_abi_enum(w, type); - break; - case category::interface_type: - write_abi_interface(w, type); - break; - case category::struct_type: - write_abi_struct(w, type); - break; - } - } - w.write_end_abi(); - } - - currentType = ""; - - // Custom additions to namespaces (REMOVED) - - auto filename = w.write_temp("%.cs", ns); - w.flush_to_file(settings.output_folder / filename); - projectionFileWritten = true; - } - } - catch (std::exception const& e) - { - writer console; - console.write("error: '%' when processing %%%\n", e.what(), ns, currentType.empty() ? "" : ".", currentType); - console.flush_to_console_error(); - throw; - } - }); - } - - if(settings.component) - { - group.add([&componentActivatableClassesByModule, &projectionFileWritten] - { - writer wm; - write_file_header(wm); - write_module_activation_factory(wm, componentActivatableClassesByModule); - wm.flush_to_file(settings.output_folder / (std::string("WinRT_Module") + ".cs")); - projectionFileWritten = true; - }); - } - - group.get(); - - if (!defaultInterfaceEntries.empty() && !settings.reference_projection) - { - std::vector> sortedEntries( - defaultInterfaceEntries.begin(), defaultInterfaceEntries.end()); - std::sort(sortedEntries.begin(), sortedEntries.end()); - write_default_interfaces_class(sortedEntries); - } - - if (!exclusiveToInterfaceEntries.empty() && settings.component && !settings.reference_projection) - { - std::vector> sortedEntries( - exclusiveToInterfaceEntries.begin(), exclusiveToInterfaceEntries.end()); - std::sort(sortedEntries.begin(), sortedEntries.end()); - write_exclusive_to_interfaces_class(sortedEntries); - } - - if (projectionFileWritten) - { - // Additional source files (REMOVED) - } - - if (settings.verbose) - { - w.write("time: %ms\n", get_elapsed_time(start)); - } - } - catch (usage_exception const&) - { - result = 1; - print_usage(w); - } - catch (std::exception const& e) - { - w.write(" error: %\n", e.what()); - result = 1; - w.flush_to_console_error(); - return result; - } - - w.flush_to_console(); - return result; - } -} - -int main(int const argc, char** argv) -{ - return cswinrt::run(argc, argv); -} diff --git a/src/cswinrt/packages.config b/src/cswinrt/packages.config deleted file mode 100644 index 93c9c42be7..0000000000 --- a/src/cswinrt/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/cswinrt/pch.cpp b/src/cswinrt/pch.cpp deleted file mode 100644 index 1d9f38c57d..0000000000 --- a/src/cswinrt/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/src/cswinrt/pch.h b/src/cswinrt/pch.h deleted file mode 100644 index da7380d537..0000000000 --- a/src/cswinrt/pch.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "cmd_reader.h" -#include "winmd_reader.h" -#include "task_group.h" -#include "text_writer.h" - -#include diff --git a/src/cswinrt/settings.h b/src/cswinrt/settings.h deleted file mode 100644 index bb5f9c3250..0000000000 --- a/src/cswinrt/settings.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -namespace cswinrt -{ - struct settings_type - { - std::set input; - std::filesystem::path output_folder; - bool verbose{}; - std::set include; - std::set exclude; - std::set addition_exclude; - winmd::reader::filter filter; - winmd::reader::filter addition_filter; - bool netstandard_compat{}; - bool component{}; - bool internal{}; - bool embedded{}; - bool public_enums{}; - bool public_exclusiveto{}; - bool idic_exclusiveto{}; - bool reference_projection{}; - }; - - extern settings_type settings; -} diff --git a/src/cswinrt/task_group.h b/src/cswinrt/task_group.h deleted file mode 100644 index 1e9f695698..0000000000 --- a/src/cswinrt/task_group.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -namespace cswinrt -{ - struct task_group - { - task_group(task_group const&) = delete; - task_group& operator=(task_group const&) = delete; - - task_group() noexcept = default; - - ~task_group() noexcept - { - for (auto&& task : m_tasks) - { - task.wait(); - } - } - - template - void add(T&& callback) - { -#if defined(_DEBUG) - callback(); -#else - m_tasks.push_back(std::async(std::forward(callback))); -#endif - } - - void get() - { - auto tasks = std::move(m_tasks); - - for (auto&& task : tasks) - { - task.wait(); - } - - for (auto&& task : tasks) - { - task.get(); - } - } - - private: - - std::vector> m_tasks; - }; -} diff --git a/src/cswinrt/text_writer.h b/src/cswinrt/text_writer.h deleted file mode 100644 index bf29d8d9c3..0000000000 --- a/src/cswinrt/text_writer.h +++ /dev/null @@ -1,520 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace cswinrt -{ - inline std::string file_to_string(std::string const& filename) - { - std::ifstream file(filename, std::ios::binary); - return static_cast(std::stringstream() << file.rdbuf()).str(); - } - - template - struct writer_base - { - writer_base(writer_base const&) = delete; - writer_base& operator=(writer_base const&) = delete; - - writer_base() - { - m_first.reserve(16 * 1024); - } - - template - void write(std::string_view const& value, Args const&... args) - { -#if defined(_DEBUG) - auto expected = count_placeholders(value); - auto actual = sizeof...(Args); - assert(expected == actual); -#endif - write_segment(value, args...); - } - - template - std::string write_temp(std::string_view const& value, Args const&... args) - { -#if defined(_DEBUG) - bool restore_debug_trace = debug_trace; - debug_trace = false; -#endif - auto const size = m_first.size(); - - assert(count_placeholders(value) == sizeof...(Args)); - write_segment(value, args...); - - std::string result{ m_first.data() + size, m_first.size() - size }; - m_first.resize(size); - -#if defined(_DEBUG) - debug_trace = restore_debug_trace; -#endif - return result; - } - - void write_impl(std::string_view const& value) - { - m_first.insert(m_first.end(), value.begin(), value.end()); - -#if defined(_DEBUG) - if (debug_trace) - { - ::printf("%.*s", static_cast(value.size()), value.data()); - } -#endif - } - - void write_impl(char const value) - { - m_first.push_back(value); - -#if defined(_DEBUG) - if (debug_trace) - { - ::printf("%c", value); - } -#endif - } - - void write(std::string_view const& value) - { - static_cast(this)->write_impl(value); - } - - void write(char const value) - { - static_cast(this)->write_impl(value); - } - - void write_code(std::string_view const& value) - { - write(value); - } - - template >> - void write(F const& f) - { - f(*static_cast(this)); - } - - void write(int32_t const value) - { - write(std::to_string(value)); - } - - void write(uint32_t const value) - { - write(std::to_string(value)); - } - - void write(int64_t const value) - { - write(std::to_string(value)); - } - - void write(uint64_t const value) - { - write(std::to_string(value)); - } - - template - void write_printf(char const* format, Args const&... args) - { - char buffer[1024]; - size_t const size = sprintf_s(buffer, format, args...); - write(std::string_view{ buffer, size }); - } - - template - void write_each(List const& list, Args const&... args) - { - for (auto&& item : list) - { - F(*static_cast(this), item, args...); - } - } - - void swap() noexcept - { - std::swap(m_second, m_first); - } - - void flush_to_console() noexcept - { - printf("%.*s", static_cast(m_first.size()), m_first.data()); - printf("%.*s", static_cast(m_second.size()), m_second.data()); - m_first.clear(); - m_second.clear(); - } - - void flush_to_console_error() noexcept - { - fprintf(stderr, "%.*s", static_cast(m_first.size()), m_first.data()); - fprintf(stderr, "%.*s", static_cast(m_second.size()), m_second.data()); - m_first.clear(); - m_second.clear(); - } - - void flush_to_file(std::string const& filename) - { - if (!file_equal(filename)) - { - std::ofstream file{ filename, std::ios::out | std::ios::binary }; - file.write(m_first.data(), m_first.size()); - file.write(m_second.data(), m_second.size()); - } - m_first.clear(); - m_second.clear(); - } - - void flush_to_file(std::filesystem::path const& filename) - { - flush_to_file(filename.string()); - } - - std::string flush_to_string() - { - std::string result; - result.reserve(m_first.size() + m_second.size()); - result.assign(m_first.begin(), m_first.end()); - result.append(m_second.begin(), m_second.end()); - m_first.clear(); - m_second.clear(); - return result; - } - - char back() - { - return m_first.empty() ? char{} : m_first.back(); - } - - bool file_equal(std::string const& filename) const - { - if (!std::filesystem::exists(filename)) - { - return false; - } - - auto file = file_to_string(filename); - - if (file.size() != m_first.size() + m_second.size()) - { - return false; - } - - if (!std::equal(m_first.begin(), m_first.end(), file.begin(), file.begin() + m_first.size())) - { - return false; - } - - return std::equal(m_second.begin(), m_second.end(), file.begin() + m_first.size(), file.end()); - } - -#if defined(_DEBUG) - bool debug_trace{}; -#endif - - private: - - static constexpr uint32_t count_placeholders(std::string_view const& format) noexcept - { - uint32_t count{}; - bool escape{}; - - for (auto c : format) - { - if (!escape) - { - if (c == '^') - { - escape = true; - continue; - } - - if (c == '%' || c == '@') - { - ++count; - } - } - escape = false; - } - - return count; - } - - void write_segment(std::string_view const& value) - { - auto offset = value.find_first_of("^"); - if (offset == std::string_view::npos) - { - write(value); - return; - } - - write(value.substr(0, offset)); - - assert(offset != value.size() - 1); - - write(value[offset + 1]); - write_segment(value.substr(offset + 2)); - } - - template - void write_segment(std::string_view const& value, First const& first, Rest const&... rest) - { - auto offset = value.find_first_of("^%@"); - assert(offset != std::string_view::npos); - write(value.substr(0, offset)); - - if (value[offset] == '^') - { - assert(offset != value.size() - 1); - - write(value[offset + 1]); - write_segment(value.substr(offset + 2), first, rest...); - } - else - { - if (value[offset] == '%') - { - static_cast(this)->write(first); - } - else - { - if constexpr (std::is_convertible_v) - { - static_cast(this)->write_code(first); - } - else - { - assert(false); // '@' placeholders are only for text. - } - } - - write_segment(value.substr(offset + 1), rest...); - } - } - - std::vector m_second; - std::vector m_first; - }; - - - template - struct indented_writer_base : public writer_base - { - void write_impl(std::string_view const& value) - { - for (auto&& c : value) - { - write_impl(c); - } - } - - void write_impl(char const value) - { - if (enable_indent) - { - update_state(value); - if (writer_base::back() == '\n' && value != '\n') - { - write_indent(); - } - } - writer_base::write_impl(value); - } - - template - std::string write_temp(std::string_view const& value, Args const& ... args) - { - auto restore_indent = enable_indent; - enable_indent = false; - auto result = writer_base::write_temp(value, args...); - enable_indent = restore_indent; - return result; - } - - void write_indent() - { - for (int32_t i = 0; i < indent; i++) - { - writer_base::write_impl(' '); - } - } - - void update_state(char const c) - { - if (state == state::open_paren_newline && c != ' ' && c != '\t') - { - indent += (scopes.back() = tab_width); - } - - switch (c) - { - case '{': - state = state::open_paren; - scopes.push_back(0); - break; - case '}': - state = state::none; - indent -= scopes.back(); - scopes.pop_back(); - break; - case '\n': - if (state == state::open_paren) - { - state = state::open_paren_newline; - } - else - { - state = state::none; - } - break; - default: - state = state::none; - break; - } - } - - enum class state - { - none, - open_paren, - open_paren_newline, - }; - state state{ state::none }; - std::vector scopes{ 0 }; - int32_t indent{}; - int32_t enable_indent{ true }; - static const int tab_width{ 4 }; - }; - - template - auto bind(Args&&... args) - { - return [&](auto& writer) - { - F(writer, args...); - }; - } - - template - auto bind(F fwrite, Args const&... args) - { - return [&, fwrite](auto& writer) - { - fwrite(writer, args...); - }; - } - - template - auto bind_each(List const& list, Args const&... args) - { - return [&](auto& writer) - { - for (auto&& item : list) - { - F(writer, item, args...); - } - }; - } - - template - auto bind_each(List const& list, Args const&... args) - { - return [&](auto& writer) - { - for (auto&& item : list) - { - writer.write(item, args...); - } - }; - } - - template - auto bind_each(F fwrite, List const& list, Args const&... args) - { - return [&, fwrite](auto& writer) - { - for (auto&& item : list) - { - fwrite(writer, item, args...); - } - }; - } - - template - auto bind_list(F fwrite, std::string_view const& delimiter, T const& list, Args const&... args) - { - return [&](auto& writer) - { - bool first{ true }; - - for (auto&& item : list) - { - if (first) - { - first = false; - } - else - { - writer.write(delimiter); - } - - fwrite(writer, item, args...); - } - }; - } - - template - auto bind_list(std::string_view const& delimiter, T const& list, Args const&... args) - { - return [&](auto& writer) - { - bool first{ true }; - - for (auto&& item : list) - { - if (first) - { - first = false; - } - else - { - writer.write(delimiter); - } - - F(writer, item, args...); - } - }; - } - - template - auto bind_list(std::string_view const& delimiter, T const& list) - { - return [&](auto& writer) - { - bool first{ true }; - - for (auto&& item : list) - { - if (first) - { - first = false; - } - else - { - writer.write(delimiter); - } - - writer.write(item); - } - }; - } -} diff --git a/src/cswinrt/type_writers.h b/src/cswinrt/type_writers.h deleted file mode 100644 index fd02a17bc5..0000000000 --- a/src/cswinrt/type_writers.h +++ /dev/null @@ -1,324 +0,0 @@ -#pragma once - -namespace cswinrt -{ - using namespace std::literals; - using namespace winmd::reader; - - template struct visit_overload : T... { using T::operator()...; }; - - template - auto call(V&& variant, C&& ...call) - { - return std::visit(visit_overload{ std::forward(call)... }, std::forward(variant)); - } - - struct writer : indented_writer_base - { - using indented_writer_base::indented_writer_base; - - std::string_view _current_namespace{}; - bool _in_abi_namespace = false; - bool _in_abi_impl_namespace = false; - - bool _check_platform = false; - std::string _platform; - - writer(std::string_view current_namespace) : - _current_namespace(current_namespace) - { - } - - void write_begin() - { - write(R"(//------------------------------------------------------------------------------ -// -// This file was generated by cswinrt.exe version % -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Windows.Foundation; -using WindowsRuntime; -using WindowsRuntime.InteropServices; -using WindowsRuntime.InteropServices.Marshalling; -using static System.Runtime.InteropServices.ComWrappers; - -#pragma warning disable CS0169 // "The field '...' is never used" -#pragma warning disable CS0649 // "Field '...' is never assigned to" -#pragma warning disable CA2207, CA1063, CA1033, CA1001, CA2213 -#pragma warning disable CSWINRT3001 // "Type or member '...' is a private implementation detail" -#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type - -)", VERSION_STRING); - } - - - void write_begin_projected() - { - _in_abi_impl_namespace = settings.component; - std::string namespacePrefix = settings.component ? "ABI.Impl." : ""; - write(R"( -namespace %% -{ -)", namespacePrefix, _current_namespace); - } - - void write_end_projected() - { - write("}\n"); - _in_abi_impl_namespace = false; - } - - void write_begin_interface_iids() - { - write(R"( -//------------------------------------------------------------------------------ -// -// This file was generated by cswinrt.exe version % -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace ABI; - -internal static class InterfaceIIDs -{ -)", VERSION_STRING - ); - } - - void write_end_interface_iids() - { - write("}\n\n"); - } - - void write_begin_abi() - { - if (!settings.netstandard_compat) - { - write("\n#pragma warning disable CA1416"); - } - write("\nnamespace ABI.%\n{\n", _current_namespace); - _in_abi_namespace = true; - } - - void write_end_abi() - { - write("}\n"); - if (!settings.netstandard_compat) - { - write("#pragma warning restore CA1416\n"); - } - _in_abi_namespace = false; - } - - using indented_writer_base::write; - - struct generic_args - { - std::vector> _stack; - size_t _scope = 0; - - struct args_guard - { - explicit args_guard(generic_args* owner = nullptr) - : _owner(owner) - { - } - - ~args_guard() - { - if (_owner) - { - _owner->pop(); - } - } - - args_guard(args_guard&& other) = delete; - args_guard& operator=(args_guard const&) = delete; - args_guard& operator=(args_guard&& other) = delete; - generic_args* _owner; - }; - - struct scope_guard - { - size_t _scope; - - explicit scope_guard(generic_args& owner, size_t scope) - : _owner(&owner), _scope(scope) - { - _scope = std::exchange(_owner->_scope, _scope); - } - - ~scope_guard() - { - if (_owner) - { - _scope = std::exchange(_owner->_scope, _scope); - } - } - - scope_guard(scope_guard&& other) - : _owner(other._owner), _scope(other._scope) - { - other._owner = nullptr; - } - - scope_guard& operator=(scope_guard const&) = delete; - scope_guard& operator=(scope_guard&& other) = delete; - generic_args* _owner; - }; - - [[nodiscard]] auto push(std::pair const& range) - { - if (empty(range)) - { - return args_guard{ nullptr }; - } - - _stack.emplace_back(begin(range), end(range)); - return args_guard{ this }; - } - - [[nodiscard]] auto push(generic_type_instance const& type) - { - XLANG_ASSERT(!type.generic_args.empty()); - _stack.push_back(type.generic_args); - return args_guard{ this }; - } - - auto get(uint32_t index) - { - size_t scope = _scope > 0 ? _scope - 1 : _stack.size(); - for(size_t i = scope; i > 0; --i) - { - auto&& args = &_stack[i-1]; - if (index >= args->size()) - { - throw_invalid("Generic index out of range"); - } - - auto& semantics = (*args)[index]; - if(auto gti = std::get_if(&semantics)) - { - index = gti->index; - continue; - } - return std::pair{ semantics, scope_guard(*this, i) }; - } - throw_invalid("No generic arguments"); - } - - void pop() - { - _stack.pop_back(); - } - }; - - generic_args _generic_args; - - [[nodiscard]] auto push_generic_params(std::pair const& range) - { - return _generic_args.push(range); - } - - [[nodiscard]] auto push_generic_args(generic_type_instance const& type) - { - return _generic_args.push(type); - } - - auto get_generic_arg_scope(uint32_t index) - { - return _generic_args.get(index); - } - - auto get_generic_arg(uint32_t index) - { - return get_generic_arg_scope(index).first; - } - - void write_code(std::string_view const& value) - { - for (auto&& c : value) - { - if (c == '`') - { - return; - } - else - { - write(c); - } - } - } - - using generic_type_name_write = std::function; - generic_type_name_write write_generic_type_name_custom{}; - struct write_generic_type_name_guard - { - writer& _writer; - generic_type_name_write _current; - write_generic_type_name_guard(writer& w, generic_type_name_write current) : - _writer(w), _current(current) - { - std::swap(_current, _writer.write_generic_type_name_custom); - } - ~write_generic_type_name_guard() - { - std::swap(_current, _writer.write_generic_type_name_custom); - } - }; - - struct write_platform_guard - { - writer& _writer; - write_platform_guard(writer& w) : _writer(w) - { - _writer._check_platform = true; - _writer._platform = {}; - } - ~write_platform_guard() - { - _writer._check_platform = false; - _writer._platform = {}; - } - }; - }; - - struct separator - { - writer& w; - std::string_view _separator{ ", " }; - bool first{ true }; - - void operator()() - { - if (first) - { - first = false; - } - else - { - w.write(_separator); - } - } - }; -} \ No newline at end of file diff --git a/src/cswinrt/version.rc b/src/cswinrt/version.rc deleted file mode 100644 index 53e96f23f063d90531d82fb05fe6e5ac5ab39584..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1558 zcmcJPNpBiK5QX2FUxDx?mLdldUwj0!2n7cOm?$TN*@A>;tN~~F^Rd6`c6(-E6xo)n zmU^bUs@|)$AO4+aqeydYb+3h%DyyR3?k?f9)0uv9Q({dt(v-cWA%9)<^^J3vz0H65 zKoagtNZM%iu*=&XT1<5b&K0W;zS#|6>Z_@1B;!|G2i7*+b#=h>$X`LO>W{u?g`_0Q zz{8E)c z>j$}LJ!yHgR64@f2KhPGX<*ZF6*4}*8esRyr)8l6^InOo_L?efh-Ha>gom-KdTMoy zl*+P*=6zKcE{Yp=>z$w(zD0foS@=$I(kGy49U#`1Y5Mn{irl|ItaW@O(}j zF_nn%eb22~Bv{x!<7UHOROn4HW5`C+O@X0rKud9qIrN6_nGu{rvNxa>emcqpvHzcK z0$V~9!CPa=PtBC*ujypV;qb-y15jgn@18vEkMeiP41CgeSR*jUugFp!_r1LDk(D)7 z+`6jX(E(PCH80ctxd*J?c}ZCxi1w5WzPvxq(5}O?<(qQs+{s=;tIjX>1@B@}XSc|0 tGgFnrbAI|J#FV^w?(!2d2Fa#lhws^bw+^z`&QGPCYWmDgwZO~Z?IS>a(9{3` From dc42b1c07486698cb4232159dc71a22bec50fb81 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 May 2026 13:52:17 -0700 Subject: [PATCH 2/9] Remove writer validation harness Delete the golden-output validation harness and its documentation for WinRT.Projection.Writer. Removes src/WinRT.Projection.Writer/eng/validate-writer-output.ps1 and the accompanying README.md which captured/validated per-scenario SHA256 manifests for emitted .cs files (capture/validate/capture-and-validate modes and baseline files). --- src/WinRT.Projection.Writer/eng/README.md | 68 -------- .../eng/validate-writer-output.ps1 | 150 ------------------ 2 files changed, 218 deletions(-) delete mode 100644 src/WinRT.Projection.Writer/eng/README.md delete mode 100644 src/WinRT.Projection.Writer/eng/validate-writer-output.ps1 diff --git a/src/WinRT.Projection.Writer/eng/README.md b/src/WinRT.Projection.Writer/eng/README.md deleted file mode 100644 index 12e76d430b..0000000000 --- a/src/WinRT.Projection.Writer/eng/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# validate-writer-output.ps1 - -A regression harness that catches accidental output drift in -`WinRT.Projection.Writer`. It runs the writer against a configured set of -scenarios (each described by an `.rsp` response file, the same format -`WinRT.Projection.Writer.TestRunner` accepts), captures a SHA256 manifest of -every emitted `.cs` file, and compares the result against a previously -captured baseline. - -## Usage - -```powershell -# First run: capture the baseline manifests for every .rsp scenario -.\validate-writer-output.ps1 -Mode capture - -# Subsequent runs: validate that the writer still produces byte-identical output -.\validate-writer-output.ps1 -Mode validate - -# Convenience: capture if no baseline exists yet, otherwise overwrite on drift -.\validate-writer-output.ps1 -Mode capture-and-validate -``` - -## Parameters - -| Parameter | Default | Purpose | -|---|---|---| -| `-Mode` | (required) | One of `capture`, `validate`, `capture-and-validate`. | -| `-RepoRoot` | the repo root, derived from the script's location | Override if running the script from outside the standard repo layout. | -| `-RspRoot` | `$RepoRoot\eng\rsp` | The directory containing the `.rsp` files. Each `.rsp` file becomes one scenario, named by its file stem. | -| `-Scenarios` | every `*.rsp` under `-RspRoot` | Restrict the scenario set when validating only a subset. | -| `-Configuration` | `Release` | The build configuration used to locate the TestRunner exe. | - -## Per-scenario manifest layout - -For every scenario, the script writes a `.sha256` file under -`$PSScriptRoot\baselines\.sha256` containing one line per emitted -`.cs` file: - -``` - -``` - -Drift is reported with file-by-file diffs (added / removed / changed). - -## Notes - -- The `.rsp` files are not committed alongside the script because they encode - paths into local `.winmd` metadata sources that vary between machines. Each - contributor sets up their own `.rsp` files for the scenarios they care - about, then captures a baseline against the writer state they consider - correct, and validates from there. -- The harness validates byte-for-byte equality of the emitted `.cs` files - against the captured baseline. If a refactor intentionally changes the - emitted formatting (whitespace, ordering, etc.) the contributor is expected - to recapture the baselines after manually reviewing that the change is - benign. - -## Why no xunit / unit test project? - -The writer's correctness is verified end-to-end via this harness rather than -through a parallel xunit test project, mirroring the convention used by -`WinRT.Interop.Generator` (which also has no xunit tests of its own — its -correctness is validated by integration-level tests in `src/Tests/`). -End-to-end byte-identity testing across the eight projection scenarios catches -real correctness regressions at a granularity that unit tests of helpers like -`IndentedTextWriter` would miss (e.g. interactions between brace-prepend -rules, namespace nesting, and the multi-line raw-string emission paths). - diff --git a/src/WinRT.Projection.Writer/eng/validate-writer-output.ps1 b/src/WinRT.Projection.Writer/eng/validate-writer-output.ps1 deleted file mode 100644 index 87f2efcfa5..0000000000 --- a/src/WinRT.Projection.Writer/eng/validate-writer-output.ps1 +++ /dev/null @@ -1,150 +0,0 @@ -# Golden-output validation harness for WinRT.Projection.Writer. -# -# Modes: -# .\validate-writer-output.ps1 -Mode capture # capture baseline manifests for the configured scenarios -# .\validate-writer-output.ps1 -Mode validate # run every scenario, compare hashes, exit non-zero on drift -# .\validate-writer-output.ps1 -Mode capture-and-validate # capture if missing; otherwise validate, overwrite on drift -# -# Per-scenario manifests are stored under: $PSScriptRoot\baselines\.sha256 -# Each scenario is described by an .rsp response file (the same format the TestRunner accepts); -# the .rsp files live under $RspRoot and select the input metadata + output directory for one -# regen scenario. The harness loads all .rsp files under $RspRoot whose name (without extension) -# matches one of the configured -Scenarios. -# -# This script is environment-portable: -RepoRoot defaults to the repo root inferred from the -# script's own location, and -RspRoot defaults to a sibling 'rsp' directory under -RepoRoot. -# Override either one when calling the script if your local layout differs. - -[CmdletBinding()] -param( - [Parameter(Mandatory=$true)] - [ValidateSet('capture','validate','capture-and-validate')] - [string]$Mode, - [string]$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..')).Path, - [string]$RspRoot = (Join-Path (Resolve-Path (Join-Path $PSScriptRoot '..\..\..')).Path 'eng\rsp'), - [string[]]$Scenarios, - [string]$Configuration = 'Release' -) - -$ErrorActionPreference = 'Stop' -$baselineDir = Join-Path $PSScriptRoot 'baselines' -if (-not (Test-Path $baselineDir)) { New-Item -ItemType Directory -Path $baselineDir | Out-Null } - -# Auto-discover scenarios from the .rsp directory if none were explicitly listed. -if (-not $Scenarios) { - if (-not (Test-Path $RspRoot)) { - throw "RspRoot '$RspRoot' does not exist. Pass -RspRoot pointing at a folder of .rsp files, or -Scenarios ." - } - $Scenarios = Get-ChildItem $RspRoot -Filter '*.rsp' | Sort-Object Name | ForEach-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Name) } - if (-not $Scenarios) { - throw "No .rsp files found under '$RspRoot'." - } -} - -# Locate the TestRunner exe. -$runner = "$RepoRoot\src\WinRT.Projection.Writer.TestRunner\bin\$Configuration\net10.0\WinRT.Projection.Writer.TestRunner.exe" -if (-not (Test-Path $runner)) { - Write-Host 'TestRunner exe not found; building...' -ForegroundColor Yellow - $proj = "$RepoRoot\src\WinRT.Projection.Writer.TestRunner\WinRT.Projection.Writer.TestRunner.csproj" - if (-not (Test-Path $proj)) { throw "TestRunner csproj not found at '$proj'." } - $buildLog = & dotnet build $proj -c $Configuration --nologo 2>&1 - if ($LASTEXITCODE -ne 0) { - Write-Host 'TestRunner build failed:' -ForegroundColor Red - $buildLog | Select-Object -Last 25 | ForEach-Object { Write-Host $_ } - throw 'TestRunner build failed.' - } - if (-not (Test-Path $runner)) { throw "TestRunner exe still not found at '$runner' after build." } -} - -Write-Host "TestRunner: $runner" -ForegroundColor Cyan - -function Get-ManifestForScenario { - param([string]$ScenarioName) - $rsp = Join-Path $RspRoot "$ScenarioName.rsp" - if (-not (Test-Path $rsp)) { return $null } - $outDir = (((Get-Content $rsp -Raw) -split "`r?`n") | Where-Object { $_ -match '^--output-directory' } | Select-Object -First 1) -replace '^--output-directory ', '' - $outDir = $outDir.Trim() - if (-not (Test-Path $outDir)) { return $null } - $sb = [System.Text.StringBuilder]::new() - Get-ChildItem "$outDir\*.cs" | Sort-Object Name | ForEach-Object { - $hash = (Get-FileHash $_.FullName -Algorithm SHA256).Hash - [void]$sb.Append($hash).Append(' ').AppendLine($_.Name) - } - return $sb.ToString() -} - -function Run-Scenario { - param([string]$ScenarioName) - $rsp = Join-Path $RspRoot "$ScenarioName.rsp" - if (-not (Test-Path $rsp)) { Write-Host "SKIP: $ScenarioName ($rsp not found)" -ForegroundColor Yellow; return $false } - $output = & $runner 'rsp' $rsp 2>&1 - if ($LASTEXITCODE -ne 0) { - Write-Host "$ScenarioName : EXEC FAILED (exit=$LASTEXITCODE)" -ForegroundColor Red - $output | Select-Object -Last 5 | ForEach-Object { Write-Host " $_" } - return $false - } - return $true -} - -$anyFailure = $false - -foreach ($scenario in $Scenarios) { - $rsp = Join-Path $RspRoot "$scenario.rsp" - if (-not (Test-Path $rsp)) { Write-Host "SKIP: $scenario (rsp missing)" -ForegroundColor Yellow; continue } - - $baselinePath = Join-Path $baselineDir "$scenario.sha256" - - if ($Mode -eq 'capture') { - if (-not (Run-Scenario $scenario)) { $anyFailure = $true; continue } - $manifest = Get-ManifestForScenario $scenario - if ($manifest -eq $null) { Write-Host "$scenario : no output dir after run" -ForegroundColor Red; $anyFailure = $true; continue } - Set-Content -Path $baselinePath -Value $manifest -NoNewline -Encoding utf8NoBOM - $count = ($manifest.TrimEnd("`r`n").Split("`n") | Measure-Object).Count - Write-Host ("{0,-40} CAPTURED files={1}" -f $scenario, $count) -ForegroundColor Green - } - elseif ($Mode -eq 'validate') { - if (-not (Test-Path $baselinePath)) { Write-Host "$scenario : NO BASELINE (run -Mode capture first)" -ForegroundColor Red; $anyFailure = $true; continue } - if (-not (Run-Scenario $scenario)) { $anyFailure = $true; continue } - $current = Get-ManifestForScenario $scenario - $baseline = Get-Content -Path $baselinePath -Raw - if ($current -eq $baseline) { - $count = ($current.TrimEnd("`r`n").Split("`n") | Measure-Object).Count - Write-Host ("{0,-40} OK files={1}" -f $scenario, $count) -ForegroundColor Green - } else { - Write-Host ("{0,-40} DRIFT" -f $scenario) -ForegroundColor Red - $baseLines = $baseline.Split("`n") | Where-Object { $_ } - $curLines = $current.Split("`n") | Where-Object { $_ } - $bMap = @{}; $cMap = @{} - foreach ($l in $baseLines) { $i = $l.IndexOf(' '); $bMap[$l.Substring($i+1).Trim()] = $l.Substring(0,$i) } - foreach ($l in $curLines) { $i = $l.IndexOf(' '); $cMap[$l.Substring($i+1).Trim()] = $l.Substring(0,$i) } - $allFiles = ($bMap.Keys + $cMap.Keys) | Sort-Object -Unique - $shown = 0 - foreach ($f in $allFiles) { - if ($shown -ge 25) { Write-Host " ... (more drifted files omitted)" -ForegroundColor DarkGray; break } - $bh = $bMap[$f]; $ch = $cMap[$f] - if (-not $bh) { Write-Host " + $f (added)" -ForegroundColor Green; $shown++ } - elseif (-not $ch) { Write-Host " - $f (removed)" -ForegroundColor Red; $shown++ } - elseif ($bh -ne $ch) { Write-Host " ~ $f (changed)" -ForegroundColor Yellow; $shown++ } - } - $anyFailure = $true - } - } - elseif ($Mode -eq 'capture-and-validate') { - if (-not (Run-Scenario $scenario)) { $anyFailure = $true; continue } - $manifest = Get-ManifestForScenario $scenario - if (-not (Test-Path $baselinePath)) { - Set-Content -Path $baselinePath -Value $manifest -NoNewline -Encoding utf8NoBOM - Write-Host ("{0,-40} CAPTURED (no prior baseline)" -f $scenario) -ForegroundColor Green - } else { - $baseline = Get-Content -Path $baselinePath -Raw - if ($manifest -eq $baseline) { - Write-Host ("{0,-40} OK" -f $scenario) -ForegroundColor Green - } else { - Set-Content -Path $baselinePath -Value $manifest -NoNewline -Encoding utf8NoBOM - Write-Host ("{0,-40} UPDATED (drift detected, baseline overwritten)" -f $scenario) -ForegroundColor Yellow - } - } - } -} - -if ($anyFailure) { exit 1 } else { exit 0 } From 659371a6dc660d225a79c1f0e6704abcc8a62944 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 May 2026 13:53:54 -0700 Subject: [PATCH 3/9] Remove WinRT Projection Writer TestRunner project Delete the WinRT.Projection.Writer.TestRunner application and its project file. Removes src/WinRT.Projection.Writer.TestRunner/Program.cs (the CLI test runner implementing compare/rsp/smoke modes and various smoke tests) and src/WinRT.Projection.Writer.TestRunner/WinRT.Projection.Writer.TestRunner.csproj to clean up the repository. --- .../Program.cs | 674 ------------------ .../WinRT.Projection.Writer.TestRunner.csproj | 13 - 2 files changed, 687 deletions(-) delete mode 100644 src/WinRT.Projection.Writer.TestRunner/Program.cs delete mode 100644 src/WinRT.Projection.Writer.TestRunner/WinRT.Projection.Writer.TestRunner.csproj diff --git a/src/WinRT.Projection.Writer.TestRunner/Program.cs b/src/WinRT.Projection.Writer.TestRunner/Program.cs deleted file mode 100644 index 4b7c0efc04..0000000000 --- a/src/WinRT.Projection.Writer.TestRunner/Program.cs +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using WindowsRuntime.ProjectionWriter; - -namespace WindowsRuntime.ProjectionWriter.TestRunner; - -internal static class Program -{ - public static int Main(string[] args) - { - // Modes: - // 1) Single .winmd path → simple test (legacy) - // 2) "compare" [--ref] → SDK projection mode - // 3) "compare-xaml" [--ref] → XAML projection mode - // 4) "compare-authoring" → component projection mode (uses fixed paths - // that mirror the .rsp file in 'authoring-projection\generated-sources\ProjectionGenerator.rsp' - // shipped in the test inputs folder). - // - // The optional [--ref] flag enables reference-projection mode (mirrors the C++ - // tool's -reference_projection flag and the CsWinRTGenerateReferenceProjection - // MSBuild property): emits a public-API-only projection without - // IWindowsRuntimeInterface markers, ABI helpers, vtables, etc. - bool refMode = Array.IndexOf(args, "--ref") >= 0; - if (args.Length >= 4 && args[0] == "compare") - { - return RunCompare(args[1], args[2], args[3], refMode); - } - if (args.Length >= 4 && args[0] == "compare-xaml") - { - return RunCompareXaml(args[1], args[2], args[3], refMode); - } - if (args.Length >= 2 && args[0] == "compare-authoring") - { - return RunCompareAuthoring(args[1]); - } - if (args.Length >= 2 && args[0] == "rsp") - { - return RunRsp(args[1], refMode); - } - if (args.Length >= 1 && args[0] == "smoke") - { - return RunSmoke(); - } - - return RunSimple(args); - } - - /// - /// Reads a `.rsp` file (matching the orchestrator's response file format) and invokes - /// with the parsed options. Used by the refactor - /// validation harness to drive the writer with input-aligned scenarios. - /// - private static int RunRsp(string rspPath, bool refMode) - { - if (!File.Exists(rspPath)) { Console.Error.WriteLine($"RSP not found: {rspPath}"); return 1; } - string text = File.ReadAllText(rspPath); - var inputs = new System.Collections.Generic.List(); - var include = new System.Collections.Generic.List(); - var exclude = new System.Collections.Generic.List(); - string? outputFolder = null; - bool component = false; - int maxDegreesOfParallelism = -1; - var tokens = new System.Collections.Generic.List(); - foreach (string raw in text.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries)) - { - string line = raw.Trim(); - if (line.Length == 0 || line.StartsWith('#')) { continue; } - int sp = line.IndexOf(' '); - if (sp < 0) { tokens.Add(line); } - else { tokens.Add(line.Substring(0, sp)); tokens.Add(line.Substring(sp + 1).Trim()); } - } - for (int i = 0; i < tokens.Count; i++) - { - string a = tokens[i]; - string? next = i + 1 < tokens.Count ? tokens[i + 1] : null; - switch (a) - { - case "--input-paths": case "--input-path": case "--input": - if (next is not null) { inputs.AddRange(next.Split(',', StringSplitOptions.RemoveEmptyEntries)); i++; } break; - case "--output-directory": case "--output-folder": case "--output": - if (next is not null) { outputFolder = next; i++; } break; - case "--include-namespaces": case "--include": - if (next is not null) { include.AddRange(next.Split(',', StringSplitOptions.RemoveEmptyEntries)); i++; } break; - case "--exclude-namespaces": case "--exclude": - if (next is not null) { exclude.AddRange(next.Split(',', StringSplitOptions.RemoveEmptyEntries)); i++; } break; - case "--component": component = true; break; - case "--reference-projection": refMode = true; break; - case "--max-degrees-of-parallelism": case "--mdop": - if (next is not null && int.TryParse(next, out int mdop)) { maxDegreesOfParallelism = mdop; i++; } break; - case "--target-framework": if (next is not null) { i++; } break; - } - } - if (outputFolder is null) { Console.Error.WriteLine("Missing --output-directory"); return 1; } - if (Directory.Exists(outputFolder)) { Directory.Delete(outputFolder, true); } - _ = Directory.CreateDirectory(outputFolder); - try - { - ProjectionWriter.Run(new ProjectionWriterOptions - { - InputPaths = inputs, OutputFolder = outputFolder, - Include = include, Exclude = exclude, - Component = component, - ReferenceProjection = refMode, Verbose = false, - MaxDegreesOfParallelism = maxDegreesOfParallelism, - }); - } - catch (Exception ex) { Console.Error.WriteLine($"ERROR: {ex.Message}"); Console.Error.WriteLine(ex.StackTrace); return 1; } - Console.WriteLine($"Generated {Directory.GetFiles(outputFolder, "*.cs", SearchOption.AllDirectories).Length} files"); - return 0; - } - - private static int RunSimple(string[] args) - { - string winmdPath = args.Length > 0 ? args[0] : @"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; - string outputFolder = args.Length > 1 ? args[1] : Path.Combine(Path.GetTempPath(), "CsWinRTPort", $"out-{DateTime.UtcNow:yyyyMMddHHmmss}"); - - if (!File.Exists(winmdPath)) - { - Console.Error.WriteLine($"Input file not found: {winmdPath}"); - return 1; - } - - Console.WriteLine($"Input: {winmdPath}"); - Console.WriteLine($"Output: {outputFolder}"); - - try - { - ProjectionWriter.Run(new ProjectionWriterOptions - { - InputPaths = new[] { winmdPath }, - OutputFolder = outputFolder, - Include = new[] { "Windows", "WindowsRuntime.Internal" }, - Verbose = true, - }); - } - catch (Exception ex) - { - Console.Error.WriteLine($"ERROR: {ex.Message}"); - Console.Error.WriteLine(ex.StackTrace); - return 1; - } - - if (Directory.Exists(outputFolder)) - { - string[] files = Directory.GetFiles(outputFolder, "*.cs", SearchOption.AllDirectories); - Console.WriteLine($"Generated {files.Length} files"); - } - return 0; - } - - /// - /// Runs the projection writer with the same options the build pipeline uses for the SDK - /// projection (the truth output to compare against). - /// - private static int RunCompare(string winmdFolder, string internalWinmd, string output, bool referenceProjection = false) - { - if (Directory.Exists(output)) - { - Directory.Delete(output, true); - } - _ = Directory.CreateDirectory(output); - - // Truth uses per-contract WinMD files (one .winmd per ApiContract). When the caller passes - // the unified UnionMetadata\Windows.winmd, redirect to the per-contract folder so the - // [WindowsRuntimeMetadata(...)] attribute argument matches truth (= contract file stem). - string resolvedWinmd = winmdFolder; - const string PerContractFolder = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windows.sdk.net.ref\10.0.26100.85-preview\winmd\windows"; - if (winmdFolder.EndsWith(@"\Windows.winmd", StringComparison.OrdinalIgnoreCase) && Directory.Exists(PerContractFolder)) - { - resolvedWinmd = PerContractFolder; - } - - try - { - ProjectionWriter.Run(new ProjectionWriterOptions - { - InputPaths = new[] { resolvedWinmd, internalWinmd }, - OutputFolder = output, - ReferenceProjection = referenceProjection, - Include = new[] - { - "Windows", - "WindowsRuntime.Internal", - "Windows.UI.Xaml.Interop", - "Windows.UI.Xaml.Data.BindableAttribute", - "Windows.UI.Xaml.Markup.ContentPropertyAttribute", - }, - Exclude = new[] - { - "Windows.UI.Colors", - "Windows.UI.ColorHelper", - "Windows.UI.IColorHelper", - "Windows.UI.IColors", - "Windows.UI.Text.FontWeights", - "Windows.UI.Text.IFontWeights", - "Windows.UI.Xaml", - "Windows.ApplicationModel.Store.Preview.WebAuthenticationCoreManagerHelper", - "Windows.ApplicationModel.Store.Preview.IWebAuthenticationCoreManagerHelper", - }, - }); - } - catch (Exception ex) - { - Console.Error.WriteLine($"ERROR: {ex.Message}"); - Console.Error.WriteLine(ex.StackTrace); - return 1; - } - - Console.WriteLine($"Generated {Directory.GetFiles(output, "*.cs").Length} files in {output}"); - return 0; - } - - /// - /// Runs the projection writer with the same options the build pipeline uses for the XAML - /// projection (the truth output to compare against). Mirrors the .rsp file in the XAML truth folder. - /// - private static int RunCompareXaml(string xamlWinmdFolder, string internalWinmd, string output, bool referenceProjection = false) - { - if (Directory.Exists(output)) - { - Directory.Delete(output, true); - } - _ = Directory.CreateDirectory(output); - - // Same as compare: prefer per-contract .winmd folder so [WindowsRuntimeMetadata(...)] matches truth. - // The XAML scenario uses the 'xaml' subfolder which contains a larger Windows.Foundation.UniversalApiContract.winmd - // that holds the Windows.UI.Xaml.* types as well. - string resolvedWinmd = xamlWinmdFolder; - const string PerContractFolder = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windows.sdk.net.ref\10.0.26100.85-preview\winmd\xaml"; - if (xamlWinmdFolder.EndsWith(@"\Windows.winmd", StringComparison.OrdinalIgnoreCase) && Directory.Exists(PerContractFolder)) - { - resolvedWinmd = PerContractFolder; - } - - try - { - ProjectionWriter.Run(new ProjectionWriterOptions - { - InputPaths = new[] { resolvedWinmd, internalWinmd }, - OutputFolder = output, - ReferenceProjection = referenceProjection, - // Mirrors the XAML projection generation .rsp: - // -exclude Windows, then -include for the specific XAML namespaces and helpers. - Include = new[] - { - "Windows.UI.Colors", - "Windows.UI.ColorHelper", - "Windows.UI.IColorHelper", - "Windows.UI.IColors", - "Windows.UI.Text.FontWeights", - "Windows.UI.Text.IFontWeights", - "Windows.UI.Xaml", - "Windows.ApplicationModel.Store.Preview.WebAuthenticationCoreManagerHelper", - "Windows.ApplicationModel.Store.Preview.IWebAuthenticationCoreManagerHelper", - }, - Exclude = new[] - { - "Windows", - "Windows.UI.Xaml.Interop", - "Windows.UI.Xaml.Data.BindableAttribute", - "Windows.UI.Xaml.Markup.ContentPropertyAttribute", - }, - }); - } - catch (Exception ex) - { - Console.Error.WriteLine($"ERROR: {ex.Message}"); - Console.Error.WriteLine(ex.StackTrace); - return 1; - } - - Console.WriteLine($"Generated {Directory.GetFiles(output, "*.cs").Length} files in {output}"); - return 0; - } - - /// - /// Runs the projection writer with the same options the build pipeline uses for component - /// authoring (the truth output to compare against). Mirrors the .rsp file in the authoring truth folder. - /// - private static int RunCompareAuthoring(string output) - { - const string AuthoringTestWinmd = @"C:\Users\sergiopedri\Downloads\authoring-projection\AuthoringTest.winmd"; - const string InternalWinmd = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windows.cswinrt\3.0.0-prerelease-ci.260430.9\metadata\WindowsRuntime.Internal.winmd"; - const string AppSdkInteractive = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windowsappsdk.interactiveexperiences\1.8.251104001\metadata\10.0.18362.0"; - const string AppSdkWinui = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windowsappsdk.winui\1.8.251105000\metadata"; - const string AppSdkFoundation = @"C:\Users\sergiopedri\.nuget\packages\microsoft.windowsappsdk.foundation\1.8.251104000\metadata"; - const string WebView2 = @"C:\Users\sergiopedri\.nuget\packages\microsoft.web.webview2\1.0.3179.45\lib\Microsoft.Web.WebView2.Core.winmd"; - const string SdkUnionMetadata = @"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; - - if (Directory.Exists(output)) - { - Directory.Delete(output, true); - } - _ = Directory.CreateDirectory(output); - - // Mirrors '-include AuthoringTest.*' from the truth .rsp - string[] includes = new[] - { - "AuthoringTest.BasicEnum", - "AuthoringTest.FlagsEnum", - "AuthoringTest.BasicDelegate", - "AuthoringTest.ComplexDelegate", - "AuthoringTest.DoubleDelegate", - "AuthoringTest.BasicClass", - "AuthoringTest.CustomWWW", - "AuthoringTest.BasicStruct", - "AuthoringTest.ComplexStruct", - "AuthoringTest.IBasicClassClass", - "AuthoringTest.CustomProperty", - "AuthoringTest.CustomPropertyStructType", - "AuthoringTest.ICustomPropertyClass", - "AuthoringTest.CustomPropertyRecordTypeFactory", - "AuthoringTest.ICustomPropertyRecordTypeFactoryStatic", - "AuthoringTest.ICustomPropertyRecordTypeFactoryClass", - "AuthoringTest.CustomPropertyProviderWithExplicitImplementation", - "AuthoringTest.CustomPropertyWithExplicitImplementation", - "AuthoringTest.IDouble", - "AuthoringTest.IAnotherInterface", - "AuthoringTest.TestClass", - "AuthoringTest.CustomDictionary", - "AuthoringTest.DisposableClass", - "AuthoringTest.IDisposableClassClass", - "AuthoringTest.ITestClassStatic", - "AuthoringTest.ITestClassFactory", - "AuthoringTest.ITestClassClass", - "AuthoringTest.CustomReadOnlyDictionary", - "AuthoringTest.ICustomReadOnlyDictionaryFactory", - "AuthoringTest.CustomVector", - "AuthoringTest.ICustomVectorFactory", - "AuthoringTest.CustomVectorView", - "AuthoringTest.ICustomVectorViewFactory", - "AuthoringTest.CustomVector2", - "AuthoringTest.ICustomVector2Factory", - "AuthoringTest.StaticClass", - "AuthoringTest.IStaticClassStatic", - "AuthoringTest.IStaticClassClass", - "AuthoringTest.ButtonUtils", - "AuthoringTest.CustomButton", - "AuthoringTest.ICustomButtonFactory", - "AuthoringTest.ICustomButtonClass", - "AuthoringTest.IButtonUtilsStatic", - "AuthoringTest.IButtonUtilsClass", - "AuthoringTest.CustomStackPanel", - "AuthoringTest.ICustomStackPanelClass", - "AuthoringTest.CustomXamlServiceProvider", - "AuthoringTest.CustomNotifyPropertyChanged", - "AuthoringTest.CustomNotifyPropertyChangedAndChanging", - "AuthoringTest.CustomCommand", - "AuthoringTest.ICustomCommandClass", - "AuthoringTest.CustomNotifyCollectionChanged", - "AuthoringTest.CustomNotifyDataErrorInfo", - "AuthoringTest.CustomNotifyDataErrorInfo2", - "AuthoringTest.CustomEnumerable", - "AuthoringTest.ICustomEnumerableFactory", - "AuthoringTest.CustomXamlMetadataProvider", - "AuthoringTest.SingleInterfaceClass", - "AuthoringTest.IDouble2", - "AuthoringTest.ExplicltlyImplementedClass", - "AuthoringTest.IExplicltlyImplementedClassClass", - "AuthoringTest.ObservableVector", - "AuthoringTest.IInterfaceInheritance", - "AuthoringTest.InterfaceInheritance", - "AuthoringTest.MultipleInterfaceMappingClass", - "AuthoringTest.CustomDictionary2", - "AuthoringTest.TestCollection", - "AuthoringTest.ITestCollectionClass", - "AuthoringTest.IPartialInterface", - "AuthoringTest.PartialClass", - "AuthoringTest.PartialStruct", - "AuthoringTest.IPartialClassFactory", - "AuthoringTest.IPartialClassClass", - "AuthoringTest.IPublicInterface", - "AuthoringTest.ICustomInterfaceGuid", - "AuthoringTest.CustomInterfaceGuidClass", - "AuthoringTest.NonActivatableType", - "AuthoringTest.INonActivatableTypeClass", - "AuthoringTest.NonActivatableFactory", - "AuthoringTest.INonActivatableFactoryStatic", - "AuthoringTest.INonActivatableFactoryClass", - "AuthoringTest.TypeOnlyActivatableViaItsOwnFactory", - "AuthoringTest.ITypeOnlyActivatableViaItsOwnFactoryStatic", - "AuthoringTest.ITypeOnlyActivatableViaItsOwnFactoryClass", - "AuthoringTest.AnotherNamespace.IOutParams", - "AuthoringTest.AnotherNamespace.NullableParamClass", - "AuthoringTest.AnotherNamespace.INullableParamClassClass", - "AuthoringTest.AnotherNamespace.MappedTypeParamClass", - "AuthoringTest.AnotherNamespace.IMappedTypeParamClassClass", - "AuthoringTest.AnotherNamespace.MixedArrayClass", - "AuthoringTest.AnotherNamespace.IMixedArrayClassClass", - "AuthoringTest.AnotherNamespace.ICustomResource", - "AuthoringTest.AnotherNamespace.DisposableResource", - "AuthoringTest.AnotherNamespace.MultiConstructorClass", - "AuthoringTest.AnotherNamespace.IMultiConstructorClassFactory", - "AuthoringTest.AnotherNamespace.IMultiConstructorClassClass", - "AuthoringTest.AnotherNamespace.StaticComplexProps", - "AuthoringTest.AnotherNamespace.IStaticComplexPropsStatic", - "AuthoringTest.AnotherNamespace.IStaticComplexPropsClass", - "AuthoringTest.AnotherNamespace.OverloadedMethodClass", - "AuthoringTest.AnotherNamespace.IOverloadedMethodClassStatic", - "AuthoringTest.AnotherNamespace.IOverloadedMethodClassClass", - "AuthoringTest.AnotherNamespace.InnerStruct", - "AuthoringTest.AnotherNamespace.OuterStruct", - "AuthoringTest.AnotherNamespace.MultiParamDelegate", - "AuthoringTest.AnotherNamespace.StructParamDelegate", - "AuthoringTest.AnotherNamespace.StructReturnDelegate", - "AuthoringTest.AnotherNamespace.DetailedFlags", - "AuthoringTest.AnotherNamespace.Priority", - "AuthoringTest.AnotherNamespace.AsyncMethodClass", - "AuthoringTest.AnotherNamespace.IAsyncMethodClassClass", - "AuthoringTest.AnotherNamespace.IVersionedInterface", - "AuthoringTest.AnotherNamespace.DeprecatedMembersClass", - "AuthoringTest.AnotherNamespace.IDeprecatedMembersClassClass", - "AuthoringTest.AnotherNamespace.NotifyWithCustomInterface", - "AuthoringTest.AnotherNamespace.INotifyWithCustomInterfaceClass", - "AuthoringTest.AnotherNamespace.FactoryAndStaticClass", - "AuthoringTest.AnotherNamespace.IFactoryAndStaticClassStatic", - "AuthoringTest.AnotherNamespace.IFactoryAndStaticClassFactory", - "AuthoringTest.AnotherNamespace.IFactoryAndStaticClassClass", - "AuthoringTest.AnotherNamespace.IFullFeaturedInterface", - "AuthoringTest.AnotherNamespace.FullFeaturedClass", - "AuthoringTest.AnotherNamespace.IFullFeaturedClassClass", - "AuthoringTest.AnotherNamespace.AnotherNamespaceContract", - "AuthoringTest.AnotherNamespace.ContractVersionedClass", - "AuthoringTest.AnotherNamespace.IContractVersionedClassClass", - "AuthoringTest.AnotherNamespace.ContractVersionedClassV2", - "AuthoringTest.AnotherNamespace.IContractVersionedClassV2Class", - "AuthoringTest.AnotherNamespace.IContractVersionedMembersV1", - "AuthoringTest.AnotherNamespace.IContractVersionedMembersV2", - "AuthoringTest.AnotherNamespace.ContractVersionedMembersClass", - "AuthoringTest.AnotherNamespace.IContractVersionedMembersClassClass", - "AuthoringTest.AnotherNamespace.IVersionedMembersV1", - "AuthoringTest.AnotherNamespace.IVersionedMembersV2", - "AuthoringTest.AnotherNamespace.VersionedMembersClass", - "AuthoringTest.AnotherNamespace.IVersionedMembersClassClass", - }; - - // Inputs: SDK + WindowsAppSDK + WinUI + WebView2 + WindowsRuntime.Internal + AuthoringTest - string[] inputs = new[] - { - SdkUnionMetadata, - AppSdkInteractive, - AppSdkWinui, - AppSdkFoundation, - WebView2, - InternalWinmd, - AuthoringTestWinmd, - }; - - try - { - ProjectionWriter.Run(new ProjectionWriterOptions - { - InputPaths = inputs, - OutputFolder = output, - Include = includes, - Exclude = new[] { "Windows" }, - Component = true, - }); - } - catch (Exception ex) - { - Console.Error.WriteLine($"ERROR: {ex.Message}"); - Console.Error.WriteLine(ex.StackTrace); - return 1; - } - - Console.WriteLine($"Generated {Directory.GetFiles(output, "*.cs").Length} files in {output}"); - return 0; - } - - /// - /// Smoke tests for the suppressEmptyLines overload on - /// 's interpolated - /// string handler. Validates that: - /// - /// When all interpolation holes on a template line expand to empty content, the line's - /// blank residue is suppressed (no stray newline emitted). - /// When at least one hole on a line emits content, no suppression occurs. - /// Literal blank lines in the template (consecutive newlines not separated by a hole) - /// are always preserved. - /// The legacy overload (without suppressEmptyLines) behaves identically to before. - /// - /// - private static int RunSmoke() - { - int failures = 0; - - WindowsRuntime.ProjectionWriter.Writers.IndentedTextWriter Make() => - new WindowsRuntime.ProjectionWriter.Writers.IndentedTextWriter(); - - // Test cases: each is (description, action_producing_string, expected_string). - var tests = new System.Collections.Generic.List<(string desc, Func actual, string expected)> - { - // Case 1: all 3 attribute holes empty → only header remains. - ("S1: all-empty middle holes collapse to nothing", - () => { - var w = Make(); - string a = string.Empty, b = string.Empty, c = string.Empty; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - {{c}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\npublic class Foo\n"), - - // Case 2: first hole non-empty, others empty → first kept, others collapsed. - ("S2: partial-empty holes collapse only the empty ones", - () => { - var w = Make(); - string a = "[A]", b = string.Empty, c = string.Empty; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - {{c}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\n[A]\npublic class Foo\n"), - - // Case 3: all holes non-empty → every line preserved. - ("S3: all-non-empty holes preserved", - () => { - var w = Make(); - string a = "[A]", b = "[B]", c = "[C]"; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - {{c}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\n[A]\n[B]\n[C]\npublic class Foo\n"), - - // Case 4: literal blank line preserved (no hole between newlines). - ("S4: literal blank line is preserved (no hole between newlines)", - () => { - var w = Make(); - string a = "[A]"; - w.WriteLine(isMultiline: true, $$""" - Line1 - - {{a}} - Line3 - """); - return w.ToString(); - }, - "Line1\n\n[A]\nLine3\n"), - - // Case 5: multiple empty holes on a single line → still collapsed. - ("S5: multiple empty holes on one line still collapse", - () => { - var w = Make(); - string a = string.Empty, b = string.Empty; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}}{{b}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\npublic class Foo\n"), - - // Case 6: hole at start of template, empty - buffer is empty so suppress rule doesn't fire. - ("S6: leading empty hole produces a leading blank line (buffer is empty)", - () => { - var w = Make(); - string a = string.Empty; - w.WriteLine(isMultiline: true, $$""" - {{a}} - public class Foo - """); - return w.ToString(); - }, - "\npublic class Foo\n"), - - // Case 7: Write (not WriteLine) - no trailing newline appended by the call. - ("S7: Write (no trailing newline) with all-empty interior holes", - () => { - var w = Make(); - string a = string.Empty, b = string.Empty; - w.Write(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\npublic class Foo"), - - // Case 8: indentation still applied to non-empty lines. - ("S8: indented writer with mixed empty/non-empty holes", - () => { - var w = Make(); - w.IncreaseIndent(); - string a = string.Empty, b = "[B]"; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - public class Foo - """); - return w.ToString(); - }, - " [Header]\n [B]\n public class Foo\n"), - - // Case 9: literal-only multiline template (no holes) is unaffected. - ("S9: literal-only template (no holes) is unaffected", - () => { - var w = Make(); - w.WriteLine(isMultiline: true, $$""" - Line1 - Line2 - - Line4 - """); - return w.ToString(); - }, - "Line1\nLine2\n\nLine4\n"), - - // Case 10: callback-style interpolation: empty callback hole is collapsed. - ("S10: empty string callback collapsed identically to empty string hole", - () => { - var w = Make(); - string a = "[A]", b = string.Empty; - w.WriteLine(isMultiline: true, $$""" - [Header] - {{a}} - {{b}} - public class Foo - """); - return w.ToString(); - }, - "[Header]\n[A]\npublic class Foo\n"), - }; - - foreach ((string desc, Func actualFn, string expected) in tests) - { - string actual = actualFn(); - bool pass = actual == expected; - if (!pass) - { - failures++; - } - - Console.WriteLine($"{(pass ? "PASS" : "FAIL")}: {desc}"); - - if (!pass) - { - Console.WriteLine($" expected: {Escape(expected)}"); - Console.WriteLine($" actual: {Escape(actual)}"); - } - } - - Console.WriteLine(); - Console.WriteLine($"=== {tests.Count - failures}/{tests.Count} smoke tests passed ==="); - - return failures == 0 ? 0 : 1; - - static string Escape(string s) => s.Replace("\\", "\\\\").Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t"); - } -} - diff --git a/src/WinRT.Projection.Writer.TestRunner/WinRT.Projection.Writer.TestRunner.csproj b/src/WinRT.Projection.Writer.TestRunner/WinRT.Projection.Writer.TestRunner.csproj deleted file mode 100644 index 8adeee69f3..0000000000 --- a/src/WinRT.Projection.Writer.TestRunner/WinRT.Projection.Writer.TestRunner.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - Exe - net10.0 - 14.0 - enable - WindowsRuntime.ProjectionWriter.TestRunner - - - - - - From 52d5a498d8df629933e09a655c2d0cf53fe17e3f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 May 2026 14:11:39 -0700 Subject: [PATCH 4/9] Update Copilot instructions and docs for the C++->C# code writers port Update the Copilot instructions and supporting .md docs to reflect the deletion of the legacy C++ 'cswinrt.exe' code writers and its replacement by the C# WinRT.Projection.Writer library (consumed in-process by the new cswinrtprojectionrefgen.exe and the existing cswinrtprojectiongen.exe). .github/copilot-instructions.md: - Repository structure: removed 'cswinrt/' entry, added WinRT.Projection.Writer/ (library) and WinRT.Projection.Ref.Generator/ (CLI tool); renumbered the 9 projects to 10. - Architecture Mermaid diagram: replaced 'cswinrt.exe' with 'cswinrtprojectionrefgen.exe' as the CsWinRTGenerateProjection front-end; added a 'Shared projection writer' callout explaining the in-process library model. - Project details: replaced the entire 'cswinrt.exe' section with two new sections - 'Projection writer' (public ProjectionWriter.Run API, options, resources/additions/base layout, internal-interop-interfaces description relocated here) and 'Reference projection generator' (Native AOT CLI tool replacing the legacy invocation in CsWinRTGenerateProjection). Renumbered the remaining sections (Impl/Projection/Interop/WinMD/Generator-tasks/SDK-projection-builds) to 5-10. Rewrote the Projection generator section to drive the writer in-process (no more 'invoke cswinrt.exe @response.rsp'). - WinRT.Runtime2 directory tree: refreshed to current layout - added Bindables, Buffers, Dispatching, Extensions, Placeholders, ProjectionDllExports, Streams, System.Runtime.InteropServices and InteropServices/Attributes; removed Windows.Foundation.Collections. - Source generator section: 4 analyzers / 9 diagnostics -> 5 analyzers / 10 diagnostics; added CSWINRT2009 (ComImportInterfaceAnalyzer warning). - Generator tasks: 4 tasks -> 5 tasks (added RunCsWinRTProjectionRefGenerator). - Code style: dropped the 'C++ project (cswinrt)' subsection (project deleted). - Naming / build-tool conventions: 4 .NET build tools -> 5; new WindowsRuntime.ProjectionWriter / WindowsRuntime.ReferenceProjectionGenerator namespaces; updated assembly-name list. - Error ID ranges: added Reference Projection Generator + Projection Writer rows; corrected ranges for Impl (0001-0014), Interop (0001-0097), WinMD (added 9999); documented the writer's 5xxx reservation within the shared CSWINRTPROJECTIONGEN prefix. - NuGet pipeline table: dropped 'CsWinRTExe' from the props row, updated CsWinRT.targets description, added Microsoft.Windows.CsWinRT.Native.targets row. - 'by cswinrt.exe' prose mentions in the custom-mapped / manually-projected sections replaced with 'by the projection writer'. docs/structure.md: - Removed the 'src/cswinrt' section. - Added 'src/WinRT.Projection.Writer' and 'src/WinRT.Projection.Ref.Generator' sections in alphabetical position. - Dropped 'cswinrt.exe' from the NuGet contents sentence. - Updated WinRT.Projection.Generator to reference the writer library. - Updated WinRT.Generator.Tasks description to include the new ref-generator task. docs/usage.md: - 'cswinrt -?' -> 'cswinrtprojectionrefgen -h' for the CLI help mention. - Updated the prose and the Mermaid edge label in the 'Generate reference projection' diagram. docs/interop.md, docs/diagnostics/cswinrt30001.md, nuget/readme.md: - Replaced 'cswinrt.exe' mentions with references to the CsWinRT projection writer (kept 'cswinrtinteropgen.exe' where mentioned, that tool is unchanged). .github/skills/update-copilot-instructions/SKILL.md (per the skill's own Step 7): - Updated the project list from 8 projects to 10. - Replaced the 'cswinrt.exe' validation entry with 'Projection writer' + 'Reference projection generator' entries. - Expanded the Generator tasks validation checklist to include RunCsWinRTProjectionRefGenerator and added a check that the projection generator path is documented as in-process (no cswinrt.exe subprocess). Remaining 'cswinrt.exe' mentions in the doc tree are intentional historical context only: - The two new sections in copilot-instructions.md and structure.md (Projection writer / Reference projection generator) reference it as 'the legacy C++ projection compiler from CsWinRT 2.x which has been deleted'. - src/Samples/NetProjectionSample/README.md keeps an old MSB3073 error example from CsWinRT 1.4.1 - left alone since it's a verbatim historical error message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 176 +++++++++++------- .../update-copilot-instructions/SKILL.md | 45 +++-- docs/diagnostics/cswinrt30001.md | 4 +- docs/interop.md | 2 +- docs/structure.md | 26 ++- docs/usage.md | 6 +- nuget/readme.md | 4 +- 7 files changed, 158 insertions(+), 105 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 489ef7a1ae..83a73c198b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -34,13 +34,14 @@ CsWinRT/ │ ├── WinRT.Runtime2/ # (1) Runtime library (WinRT.Runtime.dll) │ ├── Authoring/ │ │ └── WinRT.SourceGenerator2/ # (2) Roslyn source generator + analyzers -│ ├── cswinrt/ # (3) C++ code generator (cswinrt.exe) -│ ├── WinRT.Impl.Generator/ # (4) Impl/forwarder DLL generator (cswinrtimplgen.exe) -│ ├── WinRT.Projection.Generator/ # (5) Projection DLL generator (cswinrtprojectiongen.exe) -│ ├── WinRT.Interop.Generator/ # (6) Interop sidecar generator (cswinrtinteropgen.exe) -│ ├── WinRT.WinMD.Generator/ # (7) Component .winmd generator (cswinrtwinmdgen.exe) -│ ├── WinRT.Generator.Tasks/ # (8) MSBuild tasks for the build tools -│ └── WinRT.Sdk.Projection/ # (9) Precompiled Windows SDK projection builds +│ ├── WinRT.Projection.Writer/ # (3) Projection writer library (C# code writers) +│ ├── WinRT.Projection.Ref.Generator/ # (4) Reference projection source generator (cswinrtprojectionrefgen.exe) +│ ├── WinRT.Impl.Generator/ # (5) Impl/forwarder DLL generator (cswinrtimplgen.exe) +│ ├── WinRT.Projection.Generator/ # (6) Projection DLL generator (cswinrtprojectiongen.exe) +│ ├── WinRT.Interop.Generator/ # (7) Interop sidecar generator (cswinrtinteropgen.exe) +│ ├── WinRT.WinMD.Generator/ # (8) Component .winmd generator (cswinrtwinmdgen.exe) +│ ├── WinRT.Generator.Tasks/ # (9) MSBuild tasks for the build tools +│ └── WinRT.Sdk.Projection/ # (10) Precompiled Windows SDK projection builds ├── nuget/ # MSBuild .props/.targets for NuGet package ├── docs/ # Specifications and documentation └── eng/ # Engineering/CI infrastructure @@ -55,7 +56,7 @@ graph TD subgraph NUGET ["WinRT component NuGet package"] direction TB WINMD[".winmd metadata"] - CSWINRT["cswinrt.exe
(CsWinRTGenerateProjection target)"] + CSWINRT["cswinrtprojectionrefgen.exe
(CsWinRTGenerateProjection target)"] CS_SOURCES["Generated C# sources"] CSC_LIB["csc.exe
(compiles projection .csproj)"] REF_ASM["Reference assembly
(public API surface only)"] @@ -95,7 +96,9 @@ graph TD IMPL_DLL -.->|"referenced by app"| MY_DLL ``` -> **Precompiled SDK projections:** To speed up builds, the CsWinRT NuGet package includes precompiled `WinRT.Sdk.Projection.dll` and `WinRT.Sdk.Xaml.Projection.dll` binaries for all supported Windows SDK versions. When the CsWinRT version matches the target Windows SDK version (which is the normal case, except when using a Windows SDK preview), the projection generator skips regenerating these .dll-s entirely and uses the precompiled ones instead. This avoids the cost of running cswinrt.exe + Roslyn compilation for the entire Windows SDK on every publish. +> **Shared projection writer:** Both `cswinrtprojectionrefgen.exe` (used at component-library build time) and `cswinrtprojectiongen.exe` (used at app publish time) drive the same internal `WinRT.Projection.Writer` library to translate `.winmd` metadata into C# projection sources. The writer is consumed in-process by both tools, so there is no separate "projection compiler" executable to invoke. + +> **Precompiled SDK projections:** To speed up builds, the CsWinRT NuGet package includes precompiled `WinRT.Sdk.Projection.dll` and `WinRT.Sdk.Xaml.Projection.dll` binaries for all supported Windows SDK versions. When the CsWinRT version matches the target Windows SDK version (which is the normal case, except when using a Windows SDK preview), the projection generator skips regenerating these .dll-s entirely and uses the precompiled ones instead. This avoids the cost of running the projection writer + Roslyn compilation for the entire Windows SDK on every publish. --- @@ -145,7 +148,7 @@ By running the interop generator at the very end of the build process (after all | Property | Default | Description | |----------|---------|-------------| | `CsWinRTEnabled` | `true` | Master switch for CsWinRT processing | -| `CsWinRTGenerateProjection` | `true` | Run cswinrt.exe to generate C# projection code | +| `CsWinRTGenerateProjection` | `true` | Run `cswinrtprojectionrefgen` to generate C# projection sources for the current project | | `CsWinRTGenerateInteropAssembly2` | auto (`true` for Exe/WinExe, or Library with `PublishAot=true`) | Generate interop assemblies at publish time | | `CsWinRTGenerateReferenceProjection` | `false` | Generate reference-only projections (for NuGet packages) | | `CsWinRTComponent` | `false` | Enable Windows Runtime component authoring mode | @@ -180,26 +183,34 @@ WinRT.Runtime2/ │ ├── Windows.Foundation/ # Foundation types (Point, Rect, Size, etc.) │ └── WindowsRuntime.InteropServices/ # Bindable adapters ├── Attributes/ # Public marker attributes (e.g. [WindowsRuntimeClassName]) -├── InteropServices/ # Core interop infrastructure (~456 files) +├── InteropServices/ # Core interop infrastructure │ ├── Activation/ # Object activation factories and helpers │ ├── AsyncInfo/ # Async operation marshalling +│ ├── Attributes/ # Internal attributes consumed by the interop stack +│ ├── Bindables/ # XAML data-binding bridge types +│ ├── Buffers/ # IBuffer / Span marshalling helpers │ ├── Callbacks/ # ComWrappers callbacks │ ├── Collections/ # Collection adapters (IList↔IVector, IDictionary↔IMap, etc.) +│ ├── Dispatching/ # DispatcherQueueSynchronizationContext and dispatcher integration │ ├── Events/ # Event source infrastructure (EventSource, tokens) │ ├── Exceptions/ # Exception ↔ HRESULT marshalling +│ ├── Extensions/ # Public extension methods on projected types │ ├── InteropDllImports/ # P/Invoke declarations │ ├── Marshalers/ # Type marshallers (string, delegate, value type, etc.) │ ├── Marshalling/ # High-level marshalling APIs (WindowsRuntimeObjectMarshaller, etc.) │ ├── ObjectReference/ # Native object lifetime (WindowsRuntimeObjectReference hierarchy) +│ ├── Placeholders/ # Placeholder types for unresolved generic instantiations │ ├── Platform/ # Platform types (HRESULT, HSTRING, etc.) +│ ├── ProjectionDllExports/ # Reserved DLL entry points consumed by the generated projection │ ├── ProjectionImpls/ # Built-in interface implementations (IStringable, IPropertyValue, etc.) +│ ├── Streams/ # IRandomAccessStream / Stream interop +│ ├── System.Runtime.InteropServices/ # Custom interop attributes added to the BCL surface │ ├── TypeMapGroups/ # Type mapping group markers for ComWrappers │ ├── TypeMapInfo/ # Type metadata caching -│ ├── Vtables/ # COM vtable struct definitions (37 vtable types) +│ ├── Vtables/ # COM vtable struct definitions │ └── WeakReferences/ # Weak reference support ├── NativeObjects/ # Managed wrappers for native Windows Runtime objects (collections, async, etc.) ├── Windows.Foundation/ # Manually projected foundation types -├── Windows.Foundation.Collections/ # Collection interfaces (IObservableVector, IObservableMap, etc.) ├── Windows.Storage.Streams/ # Manually projected stream types ├── Windows.UI.Xaml.Interop/ # Manually projected XAML interop types ├── Xaml.Attributes/ # XAML-related attribute types @@ -230,7 +241,7 @@ WinRT.Runtime2/ **Types projected in WinRT.Runtime:** -Not all WinRT types are generated automatically by `cswinrt.exe` into SDK projection assemblies. `WinRT.Runtime` contains two categories of types that require special handling: +Not all WinRT types are generated automatically by the projection writer into SDK projection assemblies. `WinRT.Runtime` contains two categories of types that require special handling: #### Custom-mapped types @@ -283,7 +294,7 @@ The full mapping table (including identical-name mappings for primitives) is in #### Manually-projected types -These are WinRT types that are defined directly in `WinRT.Runtime` rather than being auto-generated by `cswinrt.exe` into SDK projection assemblies. A type is manually projected when it requires customized marshalling support, or when it is referenced by additional infrastructure code that lives in `WinRT.Runtime`. For example: +These are WinRT types that are defined directly in `WinRT.Runtime` rather than being auto-generated by the projection writer into SDK projection assemblies. A type is manually projected when it requires customized marshalling support, or when it is referenced by additional infrastructure code that lives in `WinRT.Runtime`. For example: - **Generic collection interfaces** (`IEnumerable`, `IList`, `IDictionary`, etc.) are here so that supporting adapter code (e.g. `IListAdapter`, `IEnumerableMethods`) and native object wrappers can live alongside them and be consumed by both projections and the interop generator. - **Async interfaces** (`IAsyncOperation`, `IAsyncActionWithProgress`, etc.) and their associated delegates are here to provide the async infrastructure (`AsyncInfo`, `EventSource` specializations) that bridges WinRT async patterns to `Task`. @@ -311,7 +322,7 @@ A Roslyn incremental source generator and diagnostic analyzer package. Runs at * | `CustomPropertyProviderGenerator` | `ICustomPropertyProvider` implementations for XAML data binding. Annotate types with `[GeneratedCustomPropertyProvider]` to auto-generate property accessors. Supports both UWP and WinUI XAML. | | `TypeMapAssemblyTargetGenerator` | `[TypeMapAssemblyTarget]` assembly attributes for runtime type mapping in AOT scenarios. Discovers referenced Windows Runtime assemblies and registers them with the three type map groups: `WindowsRuntimeComWrappersTypeMapGroup`, `WindowsRuntimeMetadataTypeMapGroup`, `DynamicInterfaceCastableImplementationTypeMapGroup`. | -**Four diagnostic analyzers** producing 9 diagnostics (all errors, IDs `CSWINRT2000`–`CSWINRT2008`): +**Five diagnostic analyzers** producing 10 diagnostics (IDs `CSWINRT2000`–`CSWINRT2009`; the first nine are errors, the last is a warning): Validate `[GeneratedCustomPropertyProvider]` usage: @@ -321,38 +332,79 @@ Validate `[GeneratedCustomPropertyProvider]` usage: - `CSWINRT2003`: Type already implements `ICustomPropertyProvider` members - `CSWINRT2004`–`CSWINRT2008`: Invalid attribute arguments (null names, missing properties/indexers, static indexers) -### 3. cswinrt.exe (`src/cswinrt/`) +Validate general projection usage: -A **C++ command-line tool** that reads `.winmd` metadata files and generates C# projection source code for Windows Runtime types. It uses the [WinMD NuGet package](http://aka.ms/winmd/nuget) for parsing [ECMA-335 metadata](http://www.ecma-international.org/publications/standards/Ecma-335.htm) files. +- `CSWINRT2009`: Cast to a `[ComImport]` interface type is not supported under CsWinRT 3.0 (`ComImportInterfaceAnalyzer`) -**Key files:** +### 3. Projection writer (`src/WinRT.Projection.Writer/`) -| File | Purpose | -|------|---------| -| `main.cpp` | Entry point: parses args, loads metadata, orchestrates parallel namespace generation | -| `settings.h` | Command-line option definitions (`--input`, `--output`, `--include`, `--exclude`, `--reference_projection`, etc.) | -| `code_writers.h` | Primary code generation logic (~456 KB). Contains `write_class()`, `write_interface()`, `write_struct()`, `write_enum()`, `write_delegate()` and their ABI counterparts | -| `type_writers.h` | Type name writing utilities, generic argument tracking | -| `helpers.h` | Type categorization utilities (`is_static()`, `is_type_blittable()`, `get_default_interface()`, etc.) | -| `strings/` | Embedded C# code injected into output (additions for specific namespaces) | +The **projection writer** is a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. It is the in-process replacement for the legacy C++ `cswinrt.exe` projection compiler from CsWinRT 2.x (which has been deleted). The writer ships as a library so the same code path is reused by both the reference projection generator (component build time) and the merged projection generator (app publish time). + +**Project settings:** + +- **Target**: `net10.0`, C# 14, `AllowUnsafeBlocks`, `DisableRuntimeMarshalling`, `IsAotCompatible` +- **Root namespace**: `WindowsRuntime.ProjectionWriter` +- **Assembly name**: `WinRT.Projection.Writer` +- **Dependency**: `AsmResolver.DotNet` (for `.winmd` parsing and IL/metadata helpers) +- **Public surface**: a single static `ProjectionWriter.Run(ProjectionWriterOptions)` entry point. `ProjectionWriterOptions` exposes input metadata paths, output folder, include/exclude filters, component/reference-projection/exclusive-to toggles, a logger callback, a `MaxDegreesOfParallelism` knob, and a `CancellationToken`. + +**Public API model:** -**Input/Output:** +The writer is consumed by other generators as a plain library reference — no inter-process invocation, no response file required at this boundary. The two consuming tools (`cswinrtprojectionrefgen` and `cswinrtprojectiongen`) parse their own response files, translate them into `ProjectionWriterOptions`, and call `ProjectionWriter.Run` directly. + +**Directory structure:** ``` -cswinrt.exe --input <.winmd files/dirs> --output [--include/--exclude prefixes] - [--reference_projection] [--component] [--internal] [--embedded] +WinRT.Projection.Writer/ +├── ProjectionWriter.cs # Public Run(ProjectionWriterOptions) entry point +├── ProjectionWriterOptions.cs # Public options record +├── Builders/ # Per-file emission orchestrators +├── Errors/ # WellKnownProjectionWriterException + Unhandled* (5xxx error IDs) +├── Extensions/ # AsmResolver / type-classifier extensions +├── Factories/ # ABI/projection class/interface/struct/enum/delegate factories +├── Generation/ # ProjectionGenerator orchestrator (per-namespace work items) +├── Helpers/ # Shared emission helpers (type names, IDs, signatures, blittability) +├── Metadata/ # MetadataCache, TypeSemantics, NamespaceMembers +├── Models/ # TypeKind, AbiTypeKind, MethodSignatureInfo, ParameterCategory, etc. +├── References/ # Well-known namespaces / attribute names / type names +├── Resolvers/ # TypeKindResolver, AbiTypeKindResolver, ParameterCategoryResolver, ... +├── Resources/ # Embedded baseline + per-namespace addition .cs files +│ ├── Additions/ # - One folder per WinRT namespace with hand-written add-on members +│ └── Base/ # - Always-emitted baseline: ComInteropExtensions, InspectableVftbl, ReferenceInterfaceEntries +└── Writers/ # IndentedTextWriter (with interpolated-string + callback handlers) + supporting types ``` -**Generates two layers of C# code per Windows Runtime type:** +**Generated output (per Windows Runtime type):** -1. **Projected types** (public API): the user-facing C# classes, interfaces, structs, enums, and delegates that developers use directly. Runtime classes inherit `WindowsRuntimeObject`. +1. **Projected types** (public API): user-facing C# classes, interfaces, structs, enums, and delegates. Runtime classes inherit `WindowsRuntimeObject`. 2. **ABI layer** (`namespace ABI.{Namespace}`): internal marshalling infrastructure — vtable definitions (structs with unmanaged function pointers), interface method implementations, marshaller classes. -**Namespace additions** (`strings/additions/`): extra C# code injected into specific namespaces (e.g. `Color.FromArgb()` for `Windows.UI`, XAML struct helpers for `Thickness`, `CornerRadius`, `GridLength`, etc.). +**Namespace additions** (`Resources/Additions/`): hand-authored C# snippets injected into specific namespaces (e.g. `Color.FromArgb()` for `Windows.UI`, XAML struct helpers for `Thickness`, `CornerRadius`, `GridLength`, etc.). Both WinUI (`Microsoft.UI.Xaml`) and UWP (`Windows.UI.Xaml`) variants are present. + +**Baseline emission** (`Resources/Base/`): always-emitted files that are not derived from `.winmd` metadata — `ComInteropExtensions.cs` (user-friendly extension methods wrapping internal interop interfaces), `InspectableVftbl.cs` (cached `IInspectable` vtable shape), `ReferenceInterfaceEntries.cs` (CCW interface entry table for the unknown-object fallback). -**Internal interop interfaces** (`WindowsRuntime.Internal.idl`): a manually authored IDL file defining Windows SDK COM interop interfaces (e.g. `IDisplayInformationStaticsInterop`, `IPrintManagerInterop`) that are not included in standard `.winmd` metadata. This IDL is compiled to a `.winmd` that is bundled in the CsWinRT NuGet package and passed as additional input to cswinrt.exe when building Windows SDK projections. The `[ProjectionInternal]` attribute on each interface causes all generated projection code to be `internal`. User-friendly extension methods in `strings/ComInteropExtensions.cs` wrap these internal projections, exposing discoverable APIs on the associated projected types (e.g. `DisplayInformation.GetForWindow(hwnd)`, `PrintManager.ShowPrintUIForWindowAsync(hwnd)`). +**Internal interop interfaces** (`WindowsRuntime.Internal.winmd`): a separately maintained `.winmd` of Windows SDK COM interop interfaces (e.g. `IDisplayInformationStaticsInterop`, `IPrintManagerInterop`) that are not included in standard SDK metadata. It is bundled in the CsWinRT NuGet package (`metadata/WindowsRuntime.Internal.winmd`) and added as additional input to the projection writer when building Windows SDK projections. Interfaces in this metadata carry the `[ProjectionInternal]` attribute, which causes all generated projection code for them to be emitted `internal`. The hand-written extension methods in `Resources/Base/ComInteropExtensions.cs` then surface user-friendly wrappers on the associated projected types (e.g. `DisplayInformation.GetForWindow(hwnd)`, `PrintManager.ShowPrintUIForWindowAsync(hwnd)`). -### 4. Impl generator (`src/WinRT.Impl.Generator/`) +### 4. Reference projection generator (`src/WinRT.Projection.Ref.Generator/`) + +A **.NET CLI tool** (`cswinrtprojectionrefgen.exe`) published as a **Native AOT** binary. It is the in-process C# replacement for the legacy `cswinrt.exe` invocation in the `CsWinRTGenerateProjection` MSBuild target. The tool runs at component-library build time and writes `.cs` files into the user's `$(IntermediateOutputPath)`, which `csc.exe` then compiles into the user library/component `.dll`. + +**Project settings:** + +- **Target**: `net10.0`, C# 14, `PublishAot = true`, `DisableRuntimeMarshalling` +- **Root namespace**: `WindowsRuntime.ReferenceProjectionGenerator` +- **Assembly name**: `cswinrtprojectionrefgen` +- **Dependencies**: `ConsoleAppFramework` (CLI), and a project reference to `WinRT.Projection.Writer` +- **Security**: Control Flow Guard enabled, `IlcResilient = false` + +**Two-step flow:** + +1. **Parse**: read the response file produced by the `CsWinRTGenerateProjection` MSBuild target, validate the target framework is `net10.0+`, and translate the parsed `ReferenceProjectionGeneratorArgs` into a `ProjectionWriterOptions` instance. +2. **Generate**: invoke `ProjectionWriter.Run(options)` in-process. The writer emits the projection sources into the configured output folder. + +The tool is wired through the `RunCsWinRTProjectionRefGenerator` MSBuild task (in `WinRT.Generator.Tasks`). + +### 5. Impl generator (`src/WinRT.Impl.Generator/`) A **.NET CLI tool** (`cswinrtimplgen.exe`) published as a **Native AOT** binary. Generates **forwarder/impl assemblies** that contain only type forwards (no actual code). @@ -380,15 +432,15 @@ A **.NET CLI tool** (`cswinrtimplgen.exe`) published as a **Native AOT** binary. 4. Emits `[TypeForwarder]` entries for all public top-level types, routing to the appropriate projection assembly 5. Optionally signs with a strong-name key -### 5. Projection generator (`src/WinRT.Projection.Generator/`) +### 6. Projection generator (`src/WinRT.Projection.Generator/`) -A **.NET CLI tool** (`cswinrtprojectiongen.exe`) published as a **Native AOT** binary. Takes `.winmd` files as input, invokes `cswinrt.exe` to generate C# sources, then compiles them into a projection `.dll` using the Roslyn APIs. +A **.NET CLI tool** (`cswinrtprojectiongen.exe`) published as a **Native AOT** binary. Runs at **app build / publish time** to produce a single projection `.dll` for the Windows SDK, the UWP XAML SDK, or all third-party Windows Runtime components referenced by the app. The tool drives the projection writer in-process and then compiles the resulting C# sources with Roslyn. **Project settings:** - **Target**: `net10.0`, `PublishAot = true`, `DisableRuntimeMarshalling` - **Assembly name**: `cswinrtprojectiongen` -- **Dependencies**: `AsmResolver.DotNet`, `ConsoleAppFramework`, `Microsoft.CodeAnalysis.CSharp` (Roslyn) +- **Dependencies**: `ConsoleAppFramework`, `Microsoft.CodeAnalysis.CSharp` (Roslyn), and a project reference to `WinRT.Projection.Writer` **Three projection modes:** @@ -400,11 +452,11 @@ A **.NET CLI tool** (`cswinrtprojectiongen.exe`) published as a **Native AOT** b **Three-phase pipeline:** -1. **Process References**: load reference assemblies via AsmResolver, generate `.rsp` response file with namespace filters -2. **Generate Sources**: invoke `cswinrt.exe @response.rsp` to produce C# files -3. **Emit Assembly**: parse generated `.cs` files with Roslyn, compile to `.dll` with `CSharpCompilation`, emit with embedded debug info +1. **Process References**: load reference assemblies via AsmResolver, build a `ProjectionWriterOptions` describing the inputs, output folder, and namespace filters. +2. **Generate Sources**: invoke `ProjectionWriter.Run(options)` in-process to produce C# files. +3. **Emit Assembly**: parse the generated `.cs` files with Roslyn, compile to `.dll` with `CSharpCompilation`, emit with embedded debug info. -### 6. Interop generator (`src/WinRT.Interop.Generator/`) +### 7. Interop generator (`src/WinRT.Interop.Generator/`) A **.NET CLI tool** (`cswinrtinteropgen.exe`) published as a **Native AOT** binary. This is the most complex build tool — it analyzes all application assemblies and produces the `WinRT.Interop.dll` sidecar containing all marshalling code. @@ -440,7 +492,7 @@ There's two reasons for this: **Debug repro support**: can capture all inputs into a `.zip` file for reproducible debugging. -### 7. WinMD generator (`src/WinRT.WinMD.Generator/`) +### 8. WinMD generator (`src/WinRT.WinMD.Generator/`) A **.NET CLI tool** (`cswinrtwinmdgen.exe`) published as a **Native AOT** binary. Generates a `.winmd` metadata file from a compiled C# component assembly, allowing developers to author Windows Runtime components in C#. This is a port and restructuring of the previous WinMD generator from CsWinRT 2.x, which was implemented as a Roslyn source generator. Moving it to a post-build CLI tool keeps it consistent with the other CsWinRT 3.0 build tools (interop, impl, projection generators) and removes the design-time/IntelliSense overhead of analyzing the entire component at every keystroke. It also addresses a more fundamental issue with the 2.x design: the generator produced a `.winmd` file **on disk**, but doing arbitrary file I/O from a Roslyn source generator is explicitly unsupported (source generators are only allowed to contribute additional source code to the compilation). The 2.x approach was therefore technically not even supported. The 3.0 post-build tool runs as a normal MSBuild step where file I/O is the expected output mechanism. @@ -485,7 +537,7 @@ WinRT.WinMD.Generator/ - Runs after `CoreCompile` (it needs the compiled .dll), gated on `CsWinRTComponent == true` and `DesignTimeBuild != true` - Output is `$(IntermediateOutputPath)$(AssemblyName).winmd`, then copied to `$(TargetDir)` by the authoring targets and packaged into the component's NuGet -### 8. Generator tasks (`src/WinRT.Generator.Tasks/`) +### 9. Generator tasks (`src/WinRT.Generator.Tasks/`) MSBuild task wrappers that bridge the MSBuild build system with the CLI tools above. @@ -494,10 +546,11 @@ MSBuild task wrappers that bridge the MSBuild build system with the CLI tools ab - **Target**: `netstandard2.0` (for MSBuild compatibility) - **Dependency**: `Microsoft.Build.Utilities.Core` -**Four tasks:** +**Five tasks:** | Task Class | Tool | Purpose | |------------|------|---------| +| `RunCsWinRTProjectionRefGenerator` | `cswinrtprojectionrefgen.exe` | Generate reference projection C# sources (component-library build time) | | `RunCsWinRTForwarderImplGenerator` | `cswinrtimplgen.exe` | Generate forwarder/impl assemblies | | `RunCsWinRTMergedProjectionGenerator` | `cswinrtprojectiongen.exe` | Generate merged projection assemblies | | `RunCsWinRTInteropGenerator` | `cswinrtinteropgen.exe` | Generate interop sidecar assembly | @@ -505,7 +558,7 @@ MSBuild task wrappers that bridge the MSBuild build system with the CLI tools ab All tasks extend `ToolTask`, generate response files for their respective CLI tools, and support architecture selection (`win-x86`, `win-x64`, `win-arm64`). -### 9. SDK projection builds (`src/WinRT.Sdk.Projection/`) +### 10. SDK projection builds (`src/WinRT.Sdk.Projection/`) A build project (not a tool) used during **official CsWinRT builds** to produce precompiled `WinRT.Sdk.Projection.dll` and `WinRT.Sdk.Xaml.Projection.dll` for each supported Windows SDK version. These precompiled .dll-s are bundled into the CsWinRT NuGet package so that consumers don't have to regenerate the entire Windows SDK projection on every publish (as described in the architecture overview). @@ -531,13 +584,14 @@ The MSBuild integration is orchestrated through several `.props` and `.targets` | File | Role | |------|------| -| `Microsoft.Windows.CsWinRT.props` | Initial setup: sets `CsWinRTPath`, `CsWinRTExe`, `UsingCsWinRT3` flag | +| `Microsoft.Windows.CsWinRT.props` | Initial setup: sets `CsWinRTPath`, `UsingCsWinRT3` flag, and the `BeforeMicrosoftNETSdkTargets` chain | | `Microsoft.Windows.CsWinRT.BeforeMicrosoftNetSdk.targets` | Pre-SDK configuration: reference projection mode, activation factory merging, stub exe setup | -| `Microsoft.Windows.CsWinRT.targets` | Main pipeline: projection generation (cswinrt.exe), reference setup, compilation integration | +| `Microsoft.Windows.CsWinRT.targets` | Main pipeline: invokes `cswinrtprojectionrefgen` for `CsWinRTGenerateProjection`, sets up reference inclusion, integrates with `CoreCompile` | | `Microsoft.Windows.CsWinRT.CsWinRTGen.targets` | Post-build tools: interop generation, impl generation, merged projection generation | | `Microsoft.Windows.CsWinRT.Authoring.targets` | Windows Runtime component authoring: managed DLL output, WinMD generation, NuGet packaging | | `Microsoft.Windows.CsWinRT.Authoring.Transitive.targets` | Transitive target rules for component consumers | | `Microsoft.Windows.CsWinRT.Authoring.WinMD.targets` | Component `.winmd` generation: invokes `cswinrtwinmdgen.exe` after `CoreCompile` (only when `CsWinRTComponent == true`) | +| `Microsoft.Windows.CsWinRT.Native.targets` | Imported by native (C++) `.vcxproj` consumers of C# WinRT components: detects component project references and generates `WinRT.Component.dll` + `WinRT.Interop.dll` for JIT hosting (gated on `CsWinRTDisableNativeComponentInterop != 'true'`) | --- @@ -557,26 +611,20 @@ The MSBuild integration is orchestrated through several `.props` and `.targets` - **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes), `CSWINRT3001` (obsolete internal members) - **Strong-name signing**: all assemblies signed with `src/WinRT.Runtime2/key.snk` -### C++ project (cswinrt) - -- Warnings treated as errors (`TreatWarningAsError = true`) -- Uses precompiled headers (`pch.h`/`pch.cpp`) -- Character set: Unicode -- Subsystem: Console - ### Naming conventions - C# namespaces follow the `WindowsRuntime.*` pattern (root namespace: `WindowsRuntime`) - `WindowsRuntime.InteropServices` for interop infrastructure - `WindowsRuntime.SourceGenerator` for the source generator - - `WindowsRuntime.ImplGenerator`, `WindowsRuntime.ProjectionGenerator`, `WindowsRuntime.InteropGenerator`, `WindowsRuntime.WinMDGenerator` for build tools + - `WindowsRuntime.ProjectionWriter` for the projection writer library + - `WindowsRuntime.ReferenceProjectionGenerator`, `WindowsRuntime.ProjectionGenerator`, `WindowsRuntime.ImplGenerator`, `WindowsRuntime.InteropGenerator`, `WindowsRuntime.WinMDGenerator` for build tools - ABI types live under `ABI.{OriginalNamespace}` (e.g., `ABI.System.Collections.Generic`) -- CLI tool assembly names are short: `cswinrt`, `cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtinteropgen`, `cswinrtwinmdgen` +- CLI tool assembly names are short: `cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `cswinrtimplgen`, `cswinrtinteropgen`, `cswinrtwinmdgen` - C# keywords in generated identifiers are escaped with `@` prefix ### Build tool patterns -All four .NET build tools (`cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtinteropgen`, `cswinrtwinmdgen`) share common patterns: +All five .NET build tools (`cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `cswinrtimplgen`, `cswinrtinteropgen`, `cswinrtwinmdgen`) share common patterns: - Published as **Native AOT** self-contained binaries for fast startup - Use **ConsoleAppFramework** for CLI argument parsing @@ -592,11 +640,13 @@ All four .NET build tools (`cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtint | Project | Error ID Pattern | Range | |---------|-----------------|-------| -| Source Generator | `CSWINRT2xxx` | `CSWINRT2000`–`CSWINRT2008` | -| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0010`, `9999` | -| Projection Generator | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` | -| Interop Generator | `CSWINRTINTEROPGENxxxx` | Various, `9999` | -| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0007` | +| Source Generator | `CSWINRT2xxx` | `CSWINRT2000`–`CSWINRT2009` | +| Reference Projection Generator | `CSWINRTPROJECTIONREFGENxxxx` | `0001`–`0005`, `9999` | +| Projection Generator (host) | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` | +| Projection Writer (library) | `CSWINRTPROJECTIONGEN5xxx` | `5003`–`5021`, `9999` (shares the `CSWINRTPROJECTIONGEN` prefix with the host; the writer reserves the 5000+ range so the two never collide) | +| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0014`, `9999` | +| Interop Generator | `CSWINRTINTEROPGENxxxx` | `0001`–`0097`, `9999` | +| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0007`, `9999` | | Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` | --- diff --git a/.github/skills/update-copilot-instructions/SKILL.md b/.github/skills/update-copilot-instructions/SKILL.md index e8bbbc8765..6fda82738d 100644 --- a/.github/skills/update-copilot-instructions/SKILL.md +++ b/.github/skills/update-copilot-instructions/SKILL.md @@ -19,7 +19,7 @@ Read `.github/copilot-instructions.md` in full. Take note of every factual claim ### Step 2: analyze each project in depth -Launch parallel explore agents for each of the 8 CsWinRT 3.0 projects listed in the instructions. For each project, verify: +Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in the instructions. For each project, verify: 1. **WinRT.Runtime (`src/WinRT.Runtime2/`)** - Directory structure matches what's documented @@ -30,46 +30,53 @@ Launch parallel explore agents for each of the 8 CsWinRT 3.0 projects listed in 2. **WinRT.SourceGenerator2 (`src/Authoring/WinRT.SourceGenerator2/`)** - Source generators listed still exist and generate what's described - - Diagnostic analyzer list is complete and IDs are correct (check `DiagnosticDescriptors.cs`) + - Diagnostic analyzer list is complete and IDs are correct (check `DiagnosticDescriptors.cs` and `AnalyzerReleases.Shipped.md`) - Diagnostic ID range is accurate - Project dependencies are current -3. **cswinrt.exe (`src/cswinrt/`)** - - Key files listed still exist - - Command-line options are current (check `settings.h`) - - Namespace additions in `strings/additions/` are up to date - - Generated code patterns are accurately described +3. **Projection writer (`src/WinRT.Projection.Writer/`)** + - Directory structure and namespaces match (`Builders/`, `Errors/`, `Factories/`, `Generation/`, `Helpers/`, `Metadata/`, `Models/`, `References/`, `Resolvers/`, `Resources/`, `Writers/`) + - Public API surface (`ProjectionWriter.Run`, `ProjectionWriterOptions` shape) is accurate + - Error ID range (5xxx in `Errors/WellKnownProjectionWriterExceptions.cs`) is accurate + - Resources structure (`Additions/` per-namespace + `Base/` baseline) matches -4. **Impl generator (`src/WinRT.Impl.Generator/`)** +4. **Reference projection generator (`src/WinRT.Projection.Ref.Generator/`)** + - CLI parameters on `ReferenceProjectionGeneratorArgs` are current + - Error ID range (`CSWINRTPROJECTIONREFGENxxxx`) in `Errors/WellKnownReferenceProjectionGeneratorExceptions.cs` is accurate + - Project settings (Native AOT, dependencies) are current + - MSBuild integration via `nuget/Microsoft.Windows.CsWinRT.targets` (CsWinRTGenerateProjection target → `RunCsWinRTProjectionRefGenerator`) is wired + +5. **Impl generator (`src/WinRT.Impl.Generator/`)** - Type forward routing logic is current - Project settings and dependencies are current - CLI parameters are current -5. **Projection generator (`src/WinRT.Projection.Generator/`)** +6. **Projection generator (`src/WinRT.Projection.Generator/`)** - Three projection modes are accurately described - Namespace filter logic is current - - Project settings and dependencies are current + - Project settings and dependencies (project reference to `WinRT.Projection.Writer`) are current + - The pipeline is documented as in-process (no `cswinrt.exe` subprocess invocation anymore) -6. **Interop generator (`src/WinRT.Interop.Generator/`)** +7. **Interop generator (`src/WinRT.Interop.Generator/`)** - Generated content categories are current - Directory structure and key types are accurate - Project settings and dependencies are current -7. **WinMD generator (`src/WinRT.WinMD.Generator/`)** +8. **WinMD generator (`src/WinRT.WinMD.Generator/`)** - CLI parameters on `WinMDGeneratorArgs` are current - Error ID range (`CSWINRTWINMDGENxxxx`) in `Errors/WellKnownWinMDExceptions.cs` is accurate - Project settings and dependencies are current - MSBuild integration via `nuget/Microsoft.Windows.CsWinRT.Authoring.WinMD.targets` is wired (gated on `CsWinRTComponent`) -8. **Generator tasks (`src/WinRT.Generator.Tasks/`)** - - MSBuild task classes are accurately listed (including `RunCsWinRTWinMDGenerator`) +9. **Generator tasks (`src/WinRT.Generator.Tasks/`)** + - MSBuild task classes are accurately listed (including `RunCsWinRTProjectionRefGenerator` and `RunCsWinRTWinMDGenerator`) - Task-to-tool mappings are current -9. **SDK projection builds (`src/WinRT.Sdk.Projection/`)** - - Assembly name logic (base vs XAML) is current - - Windows SDK package download and WinMD sourcing is accurate - - Build parameters (`WindowsSdkBuild`, `WindowsSdkXaml`) are current - - Project settings are current +10. **SDK projection builds (`src/WinRT.Sdk.Projection/`)** + - Assembly name logic (base vs XAML) is current + - Windows SDK package download and WinMD sourcing is accurate + - Build parameters (`WindowsSdkBuild`, `WindowsSdkXaml`) are current + - Project settings are current ### Step 3: verify the test projects diff --git a/docs/diagnostics/cswinrt30001.md b/docs/diagnostics/cswinrt30001.md index c9877706fe..ab79628ea5 100644 --- a/docs/diagnostics/cswinrt30001.md +++ b/docs/diagnostics/cswinrt30001.md @@ -1,6 +1,6 @@ # CsWinRT warning CSWINRT3001 -This type or method is a private implementation detail, and it's only meant to be consumed by generated projections (produced by 'cswinrt.exe') and by generated interop code (produced by 'cswinrtinteropgen.exe'). Private implementation detail types are not considered part of the versioned API surface, and they are ignored when determining the assembly version following semantic versioning. Types might be modified or removed across any version change for 'WinRT.Runtime.dll', and using them in user code is undefined behavior and not supported. +This type or method is a private implementation detail, and it's only meant to be consumed by generated projections (produced by the CsWinRT projection writer at build time) and by generated interop code (produced by 'cswinrtinteropgen.exe'). Private implementation detail types are not considered part of the versioned API surface, and they are ignored when determining the assembly version following semantic versioning. Types might be modified or removed across any version change for 'WinRT.Runtime.dll', and using them in user code is undefined behavior and not supported. For instance, the following sample generates CSWINRT3001: @@ -22,7 +22,7 @@ Using any private implementation detail API is not supported, and should be cons `CSWINRT30001` is emitted when user code tries to reference a type that is marked as a **private implementation detail** within `WinRT.Runtime.dll` or the generated `WinRT.Interop.dll`. These private implementation detail types exist solely to support the marshalling pipeline that CsWinRT and the .NET SDK generate at build time. They are not part of the public, versioned API surface, and consuming them from application code is unsupported. -While all of these types are public (as they are used across assembglies), they are intentionally hidden from IntelliSense and decorated with `[Obsolete]` (with `CSWINRT3001` as the diagnostic id) to warn when they are referenced. Their names often include `Impl`, `Helpers`, or other internal wording, and their diagnostic message explicitly states that they are private implementation details. During a build, `cswinrt.exe` produces projections and `cswinrtinteropgen.exe` produces `WinRT.Interop.dll`. The generated code inside these tools uses private implementation detail types to perform marshalling work. See `docs/winrt-interop-dll-spec.md` for a description of the generated interop assembly. Because the tooling controls all references to these types, their shape can change whenever needed without breaking consumers. This flexibility is what allows performance and reliability improvements across releases. +While all of these types are public (as they are used across assembglies), they are intentionally hidden from IntelliSense and decorated with `[Obsolete]` (with `CSWINRT3001` as the diagnostic id) to warn when they are referenced. Their names often include `Impl`, `Helpers`, or other internal wording, and their diagnostic message explicitly states that they are private implementation details. During a build, the CsWinRT projection writer produces the projection sources for component libraries and Windows SDK / WinUI projections, and `cswinrtinteropgen.exe` produces `WinRT.Interop.dll`. The generated code inside these tools uses private implementation detail types to perform marshalling work. See `docs/winrt-interop-dll-spec.md` for a description of the generated interop assembly. Because the tooling controls all references to these types, their shape can change whenever needed without breaking consumers. This flexibility is what allows performance and reliability improvements across releases. ## Recommended action diff --git a/docs/interop.md b/docs/interop.md index fd5b24d9c9..c358ef98b4 100644 --- a/docs/interop.md +++ b/docs/interop.md @@ -4,7 +4,7 @@ CsWinRT 3.0 provides a complete COM interop layer for Windows Runtime types on .NET 10+. All interop types are in the `WindowsRuntime.InteropServices` namespace (assembly: `WinRT.Runtime.dll`). Most marshalling is handled automatically by the generated projection and interop assemblies, but advanced scenarios may require direct use of the APIs below. -> **Note:** CsWinRT exposes many types and methods marked `[Obsolete]` with diagnostic `CSWINRT3001`. These are **private implementation details** consumed only by generated code (`cswinrt.exe` and `cswinrtinteropgen.exe`). They are not part of the versioned API surface, may change without notice, and should not be used in application code. This guide covers only the supported public APIs. +> **Note:** CsWinRT exposes many types and methods marked `[Obsolete]` with diagnostic `CSWINRT3001`. These are **private implementation details** consumed only by generated code (produced by the projection writer and `cswinrtinteropgen.exe`). They are not part of the versioned API surface, may change without notice, and should not be used in application code. This guide covers only the supported public APIs. ## Summary diff --git a/docs/structure.md b/docs/structure.md index 1a8c76b70c..a66b2dc491 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -13,7 +13,7 @@ Contains files that assist with publishing to Maestro. ## [`nuget`](../nuget) -Contains source files for producing the C#/WinRT NuGet package, which is regularly built, signed, and published to nuget.org by Microsoft. The package contains the **cswinrt.exe** projection compiler, the post-build tools (**cswinrtprojectiongen.exe**, **cswinrtimplgen.exe**, **cswinrtinteropgen.exe**, **cswinrtwinmdgen.exe**), the runtime assembly (`WinRT.Runtime.dll`), precompiled SDK projection assemblies, MSBuild `.props`/`.targets` files, and the Roslyn source generator. +Contains source files for producing the C#/WinRT NuGet package, which is regularly built, signed, and published to nuget.org by Microsoft. The package contains the post-build tools (**cswinrtprojectionrefgen.exe**, **cswinrtprojectiongen.exe**, **cswinrtimplgen.exe**, **cswinrtinteropgen.exe**, **cswinrtwinmdgen.exe**), the runtime assembly (`WinRT.Runtime.dll`), precompiled SDK projection assemblies, MSBuild `.props`/`.targets` files, and the Roslyn source generator. ## [`src/Authoring`](../src/Authoring) @@ -23,18 +23,6 @@ Contains projects for implementing authoring and hosting support, including the Contains benchmarks written using BenchmarkDotNet to track the performance of scenarios in the generated projection. To run the benchmarks using the CsWinRT projection, run `benchmark.cmd`. -## [`src/cswinrt`](../src/cswinrt) - -Contains the sources and `cswinrt.vcxproj` project file for building the C#/WinRT compiler, **cswinrt.exe**. - -The compiler uses the [WinMD NuGet package](http://aka.ms/winmd/nuget) for parsing [ECMA-335 metadata](http://www.ecma-international.org/publications/standards/Ecma-335.htm) files. The WinMD github repo includes a [winmd.natvis](https://github.com/microsoft/winmd/blob/master/vs/winmd.natvis) script for debugging metadata parsing. A symlink can be used to install the script: - > for /f "tokens=2*" %i in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Personal ^| findstr Personal') do @for /f "tokens=2" %k in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest ^| findstr catalog_productLineVersion') do @echo %j\Visual Studio %k\Visualizers| for /f "delims=" %l in ('more') do @md "%l" 2>nul & mklink "%l\winmd.natvis" "c:\git\winmd\vs\winmd.natvis" - -The C#/WinRT project also contains a cswinrt.natvis script for debugging the C# projection writing, which can also be installed with a symlink: -> for /f "tokens=2*" %i in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Personal ^| findstr Personal') do @for /f "tokens=2" %k in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest ^| findstr catalog_productLineVersion') do @echo %j\Visual Studio %k\Visualizers| for /f "delims=" %l in ('more') do @md "%l" 2>nul & mklink "%l\cswinrt.natvis" "c:\git\cswinrt\cswinrt\cswinrt.natvis" - -See also [Deploying .natvis files](https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2015#BKMK_natvis_location). - ## [`src/Perf`](../src/Perf) Contains performance-related tools, including a benchmark baseline and the IID optimizer. @@ -65,7 +53,7 @@ C#/WinRT makes use of the standalone [TestWinRT](https://github.com/microsoft/Te ## [`src/WinRT.Generator.Tasks`](../src/WinRT.Generator.Tasks) -Contains MSBuild task wrappers that invoke the CsWinRT code generators during the build. These tasks orchestrate the post-build tools — the projection generator, the impl/forwarder generator, the interop generator, and the WinMD generator — and are called from the MSBuild targets in the `nuget/` directory. +Contains MSBuild task wrappers that invoke the CsWinRT code generators during the build. These tasks orchestrate the post-build tools — the reference projection source generator, the projection generator, the impl/forwarder generator, the interop generator, and the WinMD generator — and are called from the MSBuild targets in the `nuget/` directory. ## [`src/WinRT.Impl.Generator`](../src/WinRT.Impl.Generator) @@ -77,7 +65,15 @@ Contains the **interop assembly generator** (`cswinrtinteropgen.exe`). This tool ## [`src/WinRT.Projection.Generator`](../src/WinRT.Projection.Generator) -Contains the **projection assembly generator** (`cswinrtprojectiongen.exe`). This tool runs at **app build time** and produces `WinRT.Projection.dll`, which contains the actual projection implementations for all WinRT types used by the application. The forwarder assemblies from component NuGet packages route their types into this assembly. For Windows SDK types, the CsWinRT NuGet package includes precompiled `WinRT.Sdk.Projection.dll` binaries, so this tool only needs to generate projections for third-party components. +Contains the **projection assembly generator** (`cswinrtprojectiongen.exe`). This tool runs at **app build time** and produces `WinRT.Projection.dll`, which contains the actual projection implementations for all WinRT types used by the application. The forwarder assemblies from component NuGet packages route their types into this assembly. For Windows SDK types, the CsWinRT NuGet package includes precompiled `WinRT.Sdk.Projection.dll` binaries, so this tool only needs to generate projections for third-party components. The generator drives the [`WinRT.Projection.Writer`](../src/WinRT.Projection.Writer) library in-process to produce its C# sources, then compiles them with Roslyn. + +## [`src/WinRT.Projection.Ref.Generator`](../src/WinRT.Projection.Ref.Generator) + +Contains the **reference projection source generator** (`cswinrtprojectionrefgen.exe`). This Native AOT CLI tool runs at component-library build time, driving the projection writer in-process to produce the `.cs` files that `csc.exe` then compiles into the user library/component `.dll`. It is the C# replacement for the legacy `cswinrt.exe` invocation in the `CsWinRTGenerateProjection` MSBuild target. + +## [`src/WinRT.Projection.Writer`](../src/WinRT.Projection.Writer) + +Contains the **projection writer**, a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. It is the in-process replacement for the legacy C++ `cswinrt.exe` projection compiler from CsWinRT 2.x (which has been deleted). The writer ships as a library and is consumed by both `cswinrtprojectionrefgen.exe` (component-library build time) and `cswinrtprojectiongen.exe` (app publish time) via a single `ProjectionWriter.Run(ProjectionWriterOptions)` entry point. ## [`src/WinRT.WinMD.Generator`](../src/WinRT.WinMD.Generator) diff --git a/docs/usage.md b/docs/usage.md index 4f106bcfec..12dd217e9c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -5,7 +5,7 @@ The [C#/WinRT NuGet package](https://www.nuget.org/packages/Microsoft.Windows.Cs - [Generate and distribute a projection](#generate-and-distribute-a-projection) - [Author and consume a C#/WinRT component](#author-and-consume-a-cwinrt-component) (coming soon for 3.0) -For more information on using the NuGet package, refer to the [NuGet documentation](../nuget/readme.md). Command line options can be displayed by running `cswinrt -?`. +For more information on using the NuGet package, refer to the [NuGet documentation](../nuget/readme.md). Command line options can be displayed by running `cswinrtprojectionrefgen -h`. ## Getting started with CsWinRT 3.0 @@ -56,13 +56,13 @@ Here is an example project file for a projection project that generates a refere By default, the **Windows** and **Microsoft** namespaces are not projected. The `CsWinRTGenerateReferenceProjection` property indicates this library is a projection project and configures it to generate a reference projection. For a full list of C#/WinRT NuGet project properties, refer to the [NuGet documentation](../nuget/readme.md). -In the example diagram below, the projection project invokes **cswinrt.exe** at build time, which processes `.winmd` files in the "Contoso" namespace to generate projection source files and compiles these into a reference projection assembly named `Contoso.projection.dll` under the `ref` subfolder along with a forwarder assembly with the same name. The reference and forwarder assembly is typically distributed along with the implementation assemblies (`Contoso.*.dll`) as a NuGet package. +In the example diagram below, the projection project invokes **cswinrtprojectionrefgen.exe** at build time, which processes `.winmd` files in the "Contoso" namespace to generate projection source files and compiles these into a reference projection assembly named `Contoso.projection.dll` under the `ref` subfolder along with a forwarder assembly with the same name. The reference and forwarder assembly is typically distributed along with the implementation assemblies (`Contoso.*.dll`) as a NuGet package. ```mermaid flowchart TD subgraph build ["Generate reference projection from a component"] direction LR - WINMD["Contoso.*.winmd"] -->|cswinrt.exe| CS["Contoso.*.cs\n(projection sources)"] + WINMD["Contoso.*.winmd"] -->|cswinrtprojectionrefgen.exe| CS["Contoso.*.cs\n(projection sources)"] CS -->|csc.exe| REF["ref/Contoso.projection.dll\n(Reference Assembly)"] REF -->|cswinrtimplgen.exe| FWD["Contoso.projection.dll\n(Forwarder Assembly)"] end diff --git a/nuget/readme.md b/nuget/readme.md index 392c45f1af..ecfab4b77f 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -39,7 +39,7 @@ C#/WinRT behavior can be customized with these project properties: | CsWinRTExcludes | "Windows;Microsoft" | Semicolon-separated namespaces to exclude from projection output | | CsWinRTFilters | "" | **Specifies the -includes and -excludes to include in projection output | | CsWinRTInputs | *@(ReferencePath) | Specifies WinMD files (beyond the Windows SDK) to read metadata from | -| CsWinRTParams | "" | ***Custom cswinrt.exe command-line parameters, replacing default settings below | +| CsWinRTParams | "" | ***Custom command-line parameters for the projection writer / `cswinrtprojectionrefgen`, replacing default settings below | | CsWinRTWindowsMetadata | \ \| "local" \| "sdk" \| *$(WindowsSDKVersion) | Specifies the source for Windows metadata | | CsWinRTGeneratedFilesDir | *"$(IntermediateOutputPath)\Generated Files" | Specifies the location for generated project source files | | CsWinRTMessageImportance | low \| *normal \| high | Sets the [importance](https://docs.microsoft.com/en-us/visualstudio/msbuild/message-task?view=vs-2017) of C#/WinRT build messages (see below) | @@ -94,7 +94,7 @@ The MSBuild verbosity level maps to MSBuild message importance as follows: | m[inimal] | *high | | n[ormal] | normal+ | | d[etailed], diag[nostic] | low+ | -*"high" also enables the cswinrt.exe -verbosity switch +*"high" also enables the projection writer's verbose progress messages For example, if the verbosity is set to minimal, then only messages with high importance are generated. However, if the verbosity is set to diagnostic, then all messages are generated. From fe35a91f6a0e3fe3c6e896f9a429af8f92115f49 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 7 Jun 2026 20:38:51 -0700 Subject: [PATCH 5/9] Remove leftover cswinrt.exe references Now that the C# WinRT.Internal project owns WindowsRuntime.Internal.winmd production end-to-end and no consumer depends on the legacy cswinrt.exe binary being built (or even present), purge every remaining reference to the C++ tool across the codebase: - 'nuget/Microsoft.Windows.CsWinRT.nuspec': drop the '' entry that packaged cswinrt.exe into the NuGet. - 'src/build.cmd': drop 'cswinrt_exe' variable and the 'cswinrt_exe=%cswinrt_exe%;' parameter passed to 'nuget pack'. - 'src/Directory.Build.props': drop '$(CsWinRTPath)' / '$(CsWinRTExe)' legacy build-output paths. - 'src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj' and the four call sites in 'nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets': drop the 'CsWinRTExePath="$(CsWinRTExe)"' argument passed to the 'RunCsWinRTMergedProjectionGenerator' MSBuild task. - 'nuget/Microsoft.Windows.CsWinRT.props': drop the '' property definition. - 'nuget/Microsoft.Windows.CsWinRT.Authoring.targets': drop ''. - 'nuget/Microsoft.Windows.CsWinRT.targets': drop the entire dead response-file generation subsystem ('CsWinRTCommand', 'CsWinRTCommandVerbosity', 'CsWinRTWindowsMetadataInput', 'CsWinRTPublicExclusiveTo', 'CsWinRTDynamicallyInterfaceCastableExclusiveTo' legacy override, 'CsWinRTReferenceProjection' legacy override, the 'CsWinRTParams' block, the 'WriteLinesToFile' that wrote cswinrt.exe-style args to 'cswinrt.rsp', the 'CsWinRTResponseFile' property, the 'cswinrt.rsp' file used as the target's Outputs / 'UpToDateCheckBuilt' marker, the dead 'CsWinRTCleanGenerateProjectionOutputs' target plus its 'CallTarget' / 'OnError' calls, and the 'CsWinRTProjectionExitCode' output capture). The 'RunCsWinRTProjectionRefGenerator' task constructs its own arguments directly, so none of this content was being consumed. - 'src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs': drop the 'CsWinRTExePath' property + its '[MemberNotNullWhen]' attribute + the 'AppendResponseFileCommand(..., "--cswinrt-exe-path", ...)' call. The corresponding '--cswinrt-exe-path' arg is also dropped from 'ProjectionGeneratorArgs.cs' / 'ProjectionGeneratorArgs.Parsing.cs' in 'WinRT.Projection.Generator' (the value was never consumed). - Source-file comments: trim every 'cswinrt.exe' / 'Mirrors the C++ ' mention from: - 'src/WinRT.Runtime2/Properties/WindowsRuntimeConstants.cs' (the 'PrivateImplementationDetailObsoleteMessage' now references 'cswinrtprojectiongen.exe' instead). - 'src/WinRT.Projection.Writer/' file-header banners in emitted .cs files (now read 'This file was generated by C#/WinRT version ...'). - 'src/WinRT.Projection.Writer/Helpers/SignatureGenerator.cs' xmldoc. - 'src/WinRT.Projection.Writer/WinRT.Projection.Writer.csproj' informational comment about the banner. - 'src/WinRT.Projection.Generator/Generation/ProjectionGenerator.cs' and 'ProjectionGenerator.Generate.cs' inline comments. - 'src/WinRT.Projection.Ref.Generator/Generation/ReferenceProjectionGenerator.cs' and 'ReferenceProjectionGeneratorArgs.cs' summary docs. - 'src/WinRT.Generator.Tasks/RunCsWinRTProjectionRefGenerator.cs' type doc and per-property '"Mirrors the C++ ..." doc lines. - 'src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs' activation-factory remarks. - Docs cleanup: '.github/copilot-instructions.md', 'docs/structure.md', '.github/skills/update-copilot-instructions/SKILL.md', 'src/Samples/NetProjectionSample/README.md', 'nuget/readme.md' now describe the C# projection writer / refgen tools without referencing the legacy C++ tool or its CLI args. - AzurePipelines: drop the "Stage CsWinRT" task that copied 'cswinrt.exe' / 'cswinrt.pdb' into the staging folder, remove 'native/cswinrt.exe' from the signing list in 'CsWinRT-BuildAndTest-Stage-OneBranch.yml', and drop the 'cswinrt_exe=...' property from the 'nuget pack' invocation in 'CsWinRT-PublishToNuGet-Steps.yml'. Verified end-to-end: 'dotnet build' of both 'WinRT.Internal' (produces 'WindowsRuntime.Internal.winmd') and 'WinRT.Sdk.Projection' (consumes that winmd via 'cswinrtprojectiongen.exe' to produce 'WinRT.Sdk.Projection.dll') both succeed with 0 warnings / 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 4 +- .../update-copilot-instructions/SKILL.md | 2 +- .../CsWinRT-Build-Steps.yml | 11 ---- .../CsWinRT-BuildAndTest-Stage-OneBranch.yml | 1 - .../CsWinRT-PublishToNuGet-Steps.yml | 2 +- docs/structure.md | 4 +- ...icrosoft.Windows.CsWinRT.Authoring.targets | 1 - ...crosoft.Windows.CsWinRT.CsWinRTGen.targets | 6 +- nuget/Microsoft.Windows.CsWinRT.nuspec | 1 - nuget/Microsoft.Windows.CsWinRT.props | 1 - nuget/Microsoft.Windows.CsWinRT.targets | 64 ++----------------- nuget/readme.md | 8 --- src/Directory.Build.props | 13 ---- src/Samples/NetProjectionSample/README.md | 6 +- .../RunCsWinRTMergedProjectionGenerator.cs | 7 -- .../RunCsWinRTProjectionRefGenerator.cs | 23 +++---- .../Generation/InteropGenerator.Discover.cs | 2 +- .../ProjectionGenerator.Generate.cs | 4 +- .../Generation/ProjectionGenerator.cs | 2 +- .../ProjectionGeneratorArgs.Parsing.cs | 1 - .../Generation/ProjectionGeneratorArgs.cs | 4 -- .../ReferenceProjectionGenerator.cs | 6 +- .../ReferenceProjectionGeneratorArgs.cs | 4 +- .../Extensions/ProjectionWriterExtensions.cs | 4 +- .../Factories/MetadataAttributeFactory.cs | 2 +- .../Helpers/IidExpressionGenerator.cs | 2 +- .../Helpers/SignatureGenerator.cs | 3 +- .../WinRT.Projection.Writer.csproj | 4 +- .../Properties/WindowsRuntimeConstants.cs | 2 +- .../WinRT.Sdk.Projection.csproj | 1 - src/build.cmd | 3 +- 31 files changed, 38 insertions(+), 160 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 83a73c198b..8340fff43d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -338,7 +338,7 @@ Validate general projection usage: ### 3. Projection writer (`src/WinRT.Projection.Writer/`) -The **projection writer** is a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. It is the in-process replacement for the legacy C++ `cswinrt.exe` projection compiler from CsWinRT 2.x (which has been deleted). The writer ships as a library so the same code path is reused by both the reference projection generator (component build time) and the merged projection generator (app publish time). +The **projection writer** is a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. The writer ships as a library so the same code path is reused by both the reference projection generator (component build time) and the merged projection generator (app publish time). **Project settings:** @@ -387,7 +387,7 @@ WinRT.Projection.Writer/ ### 4. Reference projection generator (`src/WinRT.Projection.Ref.Generator/`) -A **.NET CLI tool** (`cswinrtprojectionrefgen.exe`) published as a **Native AOT** binary. It is the in-process C# replacement for the legacy `cswinrt.exe` invocation in the `CsWinRTGenerateProjection` MSBuild target. The tool runs at component-library build time and writes `.cs` files into the user's `$(IntermediateOutputPath)`, which `csc.exe` then compiles into the user library/component `.dll`. +A **.NET CLI tool** (`cswinrtprojectionrefgen.exe`) published as a **Native AOT** binary. It drives the projection writer in-process from the `CsWinRTGenerateProjection` MSBuild target. The tool runs at component-library build time and writes `.cs` files into the user's `$(IntermediateOutputPath)`, which `csc.exe` then compiles into the user library/component `.dll`. **Project settings:** diff --git a/.github/skills/update-copilot-instructions/SKILL.md b/.github/skills/update-copilot-instructions/SKILL.md index 6fda82738d..bf7b5cb0ea 100644 --- a/.github/skills/update-copilot-instructions/SKILL.md +++ b/.github/skills/update-copilot-instructions/SKILL.md @@ -55,7 +55,7 @@ Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in - Three projection modes are accurately described - Namespace filter logic is current - Project settings and dependencies (project reference to `WinRT.Projection.Writer`) are current - - The pipeline is documented as in-process (no `cswinrt.exe` subprocess invocation anymore) + - The pipeline is documented as in-process (the projection writer is invoked as a library) 7. **Interop generator (`src/WinRT.Interop.Generator/`)** - Generated content categories are current diff --git a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml index a7f21658b8..e426c86bdd 100644 --- a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml +++ b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml @@ -194,17 +194,6 @@ steps: sevenZipCompression: 5 archiveFile: $(StagingFolder)\Windows\sources.zip - # Stage CsWinRT - - task: CopyFiles@2 - displayName: Stage CsWinRT - condition: and(succeeded(), and(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))) - inputs: - SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin - Contents: | - cswinrt.exe - cswinrt.pdb - TargetFolder: $(StagingFolder)\native - # Stage WindowsRuntime.Internal.winmd - task: CopyFiles@2 displayName: Stage WindowsRuntime.Internal.winmd diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml index 12dcc49074..cac7e4ae89 100644 --- a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml +++ b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml @@ -64,7 +64,6 @@ jobs: command: sign signing_profile: external_distribution files_to_sign: | - native/cswinrt.exe; net10.0/WinRT.SourceGenerator.dll; net10.0/WinRT.Host.Shim.dll; net10.0/WinRT.Runtime.dll; diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml index 22fe1c4931..b9f73b43ae 100644 --- a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml +++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml @@ -95,7 +95,7 @@ steps: command: pack searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec configurationToPack: Release - buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion) + buildProperties: cswinrt_nuget_version=$(NugetVersion);interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion) packDestination: $(ob_outputDirectory)\packages - ${{ if eq(parameters.IsGitHub, false) }}: diff --git a/docs/structure.md b/docs/structure.md index a66b2dc491..e0895712cd 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -69,11 +69,11 @@ Contains the **projection assembly generator** (`cswinrtprojectiongen.exe`). Thi ## [`src/WinRT.Projection.Ref.Generator`](../src/WinRT.Projection.Ref.Generator) -Contains the **reference projection source generator** (`cswinrtprojectionrefgen.exe`). This Native AOT CLI tool runs at component-library build time, driving the projection writer in-process to produce the `.cs` files that `csc.exe` then compiles into the user library/component `.dll`. It is the C# replacement for the legacy `cswinrt.exe` invocation in the `CsWinRTGenerateProjection` MSBuild target. +Contains the **reference projection source generator** (`cswinrtprojectionrefgen.exe`). This Native AOT CLI tool runs at component-library build time, driving the projection writer in-process to produce the `.cs` files that `csc.exe` then compiles into the user library/component `.dll`. It is invoked from the `CsWinRTGenerateProjection` MSBuild target. ## [`src/WinRT.Projection.Writer`](../src/WinRT.Projection.Writer) -Contains the **projection writer**, a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. It is the in-process replacement for the legacy C++ `cswinrt.exe` projection compiler from CsWinRT 2.x (which has been deleted). The writer ships as a library and is consumed by both `cswinrtprojectionrefgen.exe` (component-library build time) and `cswinrtprojectiongen.exe` (app publish time) via a single `ProjectionWriter.Run(ProjectionWriterOptions)` entry point. +Contains the **projection writer**, a C# library that reads `.winmd` metadata and generates C# projection source code for Windows Runtime types. The writer ships as a library and is consumed by both `cswinrtprojectionrefgen.exe` (component-library build time) and `cswinrtprojectiongen.exe` (app publish time) via a single `ProjectionWriter.Run(ProjectionWriterOptions)` entry point. ## [`src/WinRT.WinMD.Generator`](../src/WinRT.WinMD.Generator) diff --git a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets index 0dd2544db8..d33ee6e931 100644 --- a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets +++ b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets @@ -30,7 +30,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. - diff --git a/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets b/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets index 5564cdb840..3f553abc2a 100644 --- a/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets +++ b/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets @@ -343,7 +343,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. WinMDPaths="@(_WinMDPathsList)" TargetFramework="$(CsWinRTExeTFM)" WindowsMetadata="$(CsWinRTWindowsMetadata)" - CsWinRTExePath="$(CsWinRTExe)" CsWinRTToolsDirectory="$(CsWinRTMergedProjectionEffectiveToolsDirectory)" CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)" AdditionalArguments="@(CsWinRTGeneratorAdditionalArgument)" @@ -393,7 +392,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_ComponentProjectionReferenceAssemblyPaths Include="@(ReferencePath)" Condition="!$([System.String]::new('%(Identity)').EndsWith('.winmd'))" /> <_ComponentProjectionReferenceAssemblyPaths Include="@(IntermediateAssembly)" Condition="'$(CsWinRTComponent)' == 'true'" /> - + <_ComponentWinMDPaths Include="@(_WinMDPathsList)" /> <_ComponentWinMDPaths Include="$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', '$(AssemblyName).winmd'))" Condition="'$(CsWinRTComponent)' == 'true'" /> @@ -406,7 +405,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. WinMDPaths="@(_ComponentWinMDPaths)" TargetFramework="$(CsWinRTExeTFM)" WindowsMetadata="$(CsWinRTWindowsMetadata)" - CsWinRTExePath="$(CsWinRTExe)" AssemblyName="WinRT.Component" CsWinRTToolsDirectory="$(CsWinRTMergedProjectionEffectiveToolsDirectory)" CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)" @@ -482,7 +480,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. WinMDPaths="@(_SdkWinMDPathsList)" TargetFramework="$(CsWinRTExeTFM)" WindowsMetadata="$(CsWinRTWindowsMetadata)" - CsWinRTExePath="$(CsWinRTExe)" AssemblyName="WinRT.Sdk.Projection" WindowsSdkOnly="true" CsWinRTToolsDirectory="$(CsWinRTMergedProjectionEffectiveToolsDirectory)" @@ -567,7 +564,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. WinMDPaths="@(_SdkXamlWinMDPathsList)" TargetFramework="$(CsWinRTExeTFM)" WindowsMetadata="$(CsWinRTWindowsMetadata)" - CsWinRTExePath="$(CsWinRTExe)" AssemblyName="WinRT.Sdk.Xaml.Projection" WindowsSdkOnly="true" WindowsUIXamlProjection="true" diff --git a/nuget/Microsoft.Windows.CsWinRT.nuspec b/nuget/Microsoft.Windows.CsWinRT.nuspec index 999baf730d..8d39e1a566 100644 --- a/nuget/Microsoft.Windows.CsWinRT.nuspec +++ b/nuget/Microsoft.Windows.CsWinRT.nuspec @@ -21,7 +21,6 @@ - diff --git a/nuget/Microsoft.Windows.CsWinRT.props b/nuget/Microsoft.Windows.CsWinRT.props index 9e60305734..a5a28256a2 100644 --- a/nuget/Microsoft.Windows.CsWinRT.props +++ b/nuget/Microsoft.Windows.CsWinRT.props @@ -7,7 +7,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', '..')) - $(CsWinRTPath)tools\win-x86\cswinrt.exe true diff --git a/nuget/Microsoft.Windows.CsWinRT.targets b/nuget/Microsoft.Windows.CsWinRT.targets index e199ab83ff..18151c0538 100644 --- a/nuget/Microsoft.Windows.CsWinRT.targets +++ b/nuget/Microsoft.Windows.CsWinRT.targets @@ -7,7 +7,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. normal - -verbose $(ResolveAssemblyReferencesDependsOn);CsWinRTRemoveWindowsReference true false @@ -210,11 +209,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. - - - - - - "$(CsWinRTExe)" %40"$(CsWinRTResponseFile)" - -input $(CsWinRTWindowsMetadata) - + Condition="'$(CsWinRTGenerateProjection)' == 'true'"> - @@ -261,39 +244,17 @@ Copyright (C) Microsoft Corporation. All rights reserved. $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..\metadata\WindowsRuntime.Internal.winmd')) - --input $(CsWinRTInteropMetadata) --include WindowsRuntime.Internal - - - -public_exclusiveto - -idic_exclusiveto - -reference_projection - - -$(CsWinRTCommandVerbosity) --target $(CsWinRTExeTFM) -$(CsWinRTWindowsMetadataInput) --input @(CsWinRTInputs->'"%(FullPath)"', ' ') --output "$(CsWinRTGeneratedFilesDir.TrimEnd('\'))" -$(CsWinRTFilters) -$(CsWinRTIncludeWinRTInterop) -$(CsWinRTPublicExclusiveTo) -$(CsWinRTDynamicallyInterfaceCastableExclusiveTo) -$(CsWinRTReferenceProjection) - false true - 0 @@ -301,13 +262,12 @@ $(CsWinRTReferenceProjection) - @@ -322,7 +282,7 @@ $(CsWinRTReferenceProjection) <_CsWinRTRefHasWindows Include="@(_CsWinRTRefIncludes)" Condition="'%(Identity)' == 'Windows'" /> @@ -356,19 +316,7 @@ $(CsWinRTReferenceProjection) ReferenceProjection="$(CsWinRTGenerateReferenceProjection)" CsWinRTToolsDirectory="$(CsWinRTRefGenEffectiveToolsDirectory)" CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)" - ContinueOnError="$(CsWinRTContinueOnError)"> - - - - - - - - - - - - + ContinueOnError="$(CsWinRTContinueOnError)" /> diff --git a/nuget/readme.md b/nuget/readme.md index ecfab4b77f..fa9f165dad 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -39,7 +39,6 @@ C#/WinRT behavior can be customized with these project properties: | CsWinRTExcludes | "Windows;Microsoft" | Semicolon-separated namespaces to exclude from projection output | | CsWinRTFilters | "" | **Specifies the -includes and -excludes to include in projection output | | CsWinRTInputs | *@(ReferencePath) | Specifies WinMD files (beyond the Windows SDK) to read metadata from | -| CsWinRTParams | "" | ***Custom command-line parameters for the projection writer / `cswinrtprojectionrefgen`, replacing default settings below | | CsWinRTWindowsMetadata | \ \| "local" \| "sdk" \| *$(WindowsSDKVersion) | Specifies the source for Windows metadata | | CsWinRTGeneratedFilesDir | *"$(IntermediateOutputPath)\Generated Files" | Specifies the location for generated project source files | | CsWinRTMessageImportance | low \| *normal \| high | Sets the [importance](https://docs.microsoft.com/en-us/visualstudio/msbuild/message-task?view=vs-2017) of C#/WinRT build messages (see below) | @@ -50,13 +49,6 @@ C#/WinRT behavior can be customized with these project properties: * -exclude $(CsWinRTExcludes) * -include $(CsWinRTIncludes) -***If CsWinRTParams is not defined, the following effective value is used: -* -target $(TargetFramework) -* -input $(CsWinRTWindowsMetadata) -* -input @(CsWinRTInputs) -* -output $(CsWinRTGeneratedFilesDir) -* $(CsWinRTFilters) - ## Runtime feature switches CsWinRT provides runtime feature switches that allow opt-in/opt-out of specific functionality. When a feature is disabled, all code behind that switch is dead-code-eliminated by the trimmer, making features fully pay-for-play. See the [AOT and trimming documentation](../docs/aot-trimming.md) for more details. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d615e4804e..e05e1c2668 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -63,19 +63,6 @@ x86 $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)_build', '$(BuildPlatform)', '$(Configuration)')) - - $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', 'cswinrt', 'bin')) - $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)_build', 'x86', '$(Configuration)', 'cswinrt', 'bin')) - $(CsWinRTPath)cswinrt.exe - $(VersionString) diff --git a/src/WinRT.Runtime2/Properties/WindowsRuntimeConstants.cs b/src/WinRT.Runtime2/Properties/WindowsRuntimeConstants.cs index e871e10332..c6b39f72c7 100644 --- a/src/WinRT.Runtime2/Properties/WindowsRuntimeConstants.cs +++ b/src/WinRT.Runtime2/Properties/WindowsRuntimeConstants.cs @@ -12,7 +12,7 @@ internal static class WindowsRuntimeConstants /// A message for private implementation detail types. /// public const string PrivateImplementationDetailObsoleteMessage = - "This type or method is a private implementation detail, and it's only meant to be consumed by generated projections (produced by 'cswinrt.exe') " + + "This type or method is a private implementation detail, and it's only meant to be consumed by generated projections (produced by 'cswinrtprojectiongen.exe') " + "and by generated interop code (produced by 'cswinrtinteropgen.exe'). Private implementation detail types are not considered part of the versioned " + "API surface, and they are ignored when determining the assembly version following semantic versioning. Types might be modified or removed " + "across any version change for 'WinRT.Runtime.dll', and using them in user code is undefined behavior and not supported."; diff --git a/src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj b/src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj index aee23e661c..6332d84096 100644 --- a/src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj +++ b/src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj @@ -114,7 +114,6 @@ WinMDPaths="@(_SdkWinMDPaths)" TargetFramework="net10.0" WindowsMetadata="$(_WinMDFolder)" - CsWinRTExePath="$(CsWinRTExe)" AssemblyName="$(AssemblyName)" WindowsSdkOnly="true" WindowsUIXamlProjection="$(WindowsSdkXaml)" diff --git a/src/build.cmd b/src/build.cmd index e37bb39a60..d94c32c817 100644 --- a/src/build.cmd +++ b/src/build.cmd @@ -250,7 +250,6 @@ if "%cswinrt_label%"=="functionaltest" exit /b 0 :package rem We set the properties of the CsWinRT.nuspec here, and pass them as the -Properties option when we call `nuget pack` set cswinrt_bin_dir=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\bin\ -set cswinrt_exe=%cswinrt_bin_dir%cswinrt.exe set interop_winmd=%this_dir%WinRT.Internal\bin\%cswinrt_configuration%\net10.0-windows10.0.26100.1\WindowsRuntime.Internal.winmd set net10_runtime=%this_dir%WinRT.Runtime2\bin\%cswinrt_configuration%\net10.0\WinRT.Runtime.dll set net10_runtime_xml=%this_dir%WinRT.Runtime2\bin\%cswinrt_configuration%\net10.0\WinRT.Runtime.xml @@ -267,7 +266,7 @@ set run_cswinrt_generator_task=%this_dir%WinRT.Generator.Tasks\bin\%cswinrt_conf rem Now call pack echo Creating nuget package -call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;net10_runtime=%net10_runtime%;net10_runtime_xml=%net10_runtime_xml%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;cswinrtinteropgen_x64=%cswinrtinteropgen_x64%;cswinrtinteropgen_arm64=%cswinrtinteropgen_arm64%;cswinrtimplgen_x64=%cswinrtimplgen_x64%;cswinrtimplgen_arm64=%cswinrtimplgen_arm64%;cswinrtprojectiongen_x64=%cswinrtprojectiongen_x64%;cswinrtprojectiongen_arm64=%cswinrtprojectiongen_arm64%;cswinrtprojectionrefgen_x64=%cswinrtprojectionrefgen_x64%;cswinrtprojectionrefgen_arm64=%cswinrtprojectionrefgen_arm64%;run_cswinrt_generator_task=%run_cswinrt_generator_task%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis +call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties interop_winmd=%interop_winmd%;net10_runtime=%net10_runtime%;net10_runtime_xml=%net10_runtime_xml%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;cswinrtinteropgen_x64=%cswinrtinteropgen_x64%;cswinrtinteropgen_arm64=%cswinrtinteropgen_arm64%;cswinrtimplgen_x64=%cswinrtimplgen_x64%;cswinrtimplgen_arm64=%cswinrtimplgen_arm64%;cswinrtprojectiongen_x64=%cswinrtprojectiongen_x64%;cswinrtprojectiongen_arm64=%cswinrtprojectiongen_arm64%;cswinrtprojectionrefgen_x64=%cswinrtprojectionrefgen_x64%;cswinrtprojectionrefgen_arm64=%cswinrtprojectionrefgen_arm64%;run_cswinrt_generator_task=%run_cswinrt_generator_task%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis goto :eof :exec From d299cf4b1eefd235e7db44d21e66811baf5e335b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 7 Jun 2026 21:07:08 -0700 Subject: [PATCH 6/9] Refresh Copilot instructions for WinRT.Internal and post-cswinrt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update '.github/copilot-instructions.md' and the matching update skill to reflect the current 11-project structure and the complete removal of the legacy C++ cswinrt.exe tool from this branch. copilot-instructions.md: - Repository structure tree: add 'WinRT.Internal/' as project 11. - WinRT.Runtime2 directory tree: add the actual nested subdirectories that were missing — 'ABI/Windows.Storage.Streams/', 'Windows.Foundation/{Collections,Extensions,Metadata}/', 'Windows.Storage.Streams/Extensions/', 'Exceptions/{Microsoft.UI.Xaml,Windows.UI.Xaml}/', and inline notes for the second-level subdirs under 'InteropServices/{AsyncInfo,Buffers, Marshalling,Streams}/'. - WinRT.SourceGenerator2 project settings: fix the documented assembly name from 'WinRT.SourceGenerator2' to 'WinRT.SourceGenerator' (the produced .dll name; the project folder retains the '2' suffix for repo history). - Projection writer directory tree: add the previously missing top-level 'Attributes/' folder and 'Extensions/' entry. - Projection writer "Internal interop interfaces" paragraph: rewrite to describe 'WindowsRuntime.Internal.winmd' as produced from the C# WinRT.Internal project rather than as a "separately maintained" file. - Add a new project section "11. WinRT.Internal" describing its role (producer of 'WindowsRuntime.Internal.winmd'), project settings (TFM 'net10.0-windows10.0.26100.1', disabled CsWinRT integration, 'IsPackable=false', pinned 'WindowsSdkPackageVersion'), contents (HWND struct, 'ProjectionInternalAttribute', and the 14 'I*Interop' C# source files), and MSBuild integration (the 'GenerateWindowsRuntimeInternalWinMD' target invokes 'cswinrtwinmdgen.exe' via '' to sidestep the 'MSB3027' file-lock issue, and wires the output via '$(CsWinRTInteropMetadata)'). - Naming conventions: add 'WindowsRuntime.Internal' to the namespace list as the interop metadata authoring project. - Other directories tests row: extend the test-project list with all current projects discovered under 'src/Tests/' (AuthoringWuxTest, AuthoringConsumptionTest, AuthoringWinUITest, AuthoringWuxConsumptionTest, BuildDeterminismTest, DiagnosticTests, RuntimeFrameworkVersion, OOPExe, HostTest), and correct 'ObjectLifetimeTests/' to 'ObjectLifetimeTests.Lifted/'. update-copilot-instructions/SKILL.md: - Bump the project count in step 2 from 10 to 11. - Add the new project 11 (WinRT.Internal) with its validation checklist. - Add a note to project 2's checklist about the assembly-name vs folder-name mismatch. - Add a note to project 3's checklist that 'Attributes/' and 'Extensions/' are top-level dirs in the projection writer. - Add notes to projects 6 and 9 to verify there is no leftover 'CsWinRTExePath' field / parameter (a regression we hit during the cswinrt cleanup). - Add 'SdkPackageVersion' to project 10's build parameters list. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 50 +++++++++++++++---- .../update-copilot-instructions/SKILL.md | 16 ++++-- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8340fff43d..f6581cd656 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -41,7 +41,8 @@ CsWinRT/ │ ├── WinRT.Interop.Generator/ # (7) Interop sidecar generator (cswinrtinteropgen.exe) │ ├── WinRT.WinMD.Generator/ # (8) Component .winmd generator (cswinrtwinmdgen.exe) │ ├── WinRT.Generator.Tasks/ # (9) MSBuild tasks for the build tools -│ └── WinRT.Sdk.Projection/ # (10) Precompiled Windows SDK projection builds +│ ├── WinRT.Sdk.Projection/ # (10) Precompiled Windows SDK projection builds +│ └── WinRT.Internal/ # (11) WindowsRuntime.Internal.winmd authoring project ├── nuget/ # MSBuild .props/.targets for NuGet package ├── docs/ # Specifications and documentation └── eng/ # Engineering/CI infrastructure @@ -181,14 +182,15 @@ WinRT.Runtime2/ ├── ABI/ # ABI type mappings (managed ↔ native) │ ├── System/ # Primitives, String, Uri, DateTimeOffset, collections, etc. │ ├── Windows.Foundation/ # Foundation types (Point, Rect, Size, etc.) +│ ├── Windows.Storage.Streams/ # Stream ABI mappings │ └── WindowsRuntime.InteropServices/ # Bindable adapters ├── Attributes/ # Public marker attributes (e.g. [WindowsRuntimeClassName]) ├── InteropServices/ # Core interop infrastructure │ ├── Activation/ # Object activation factories and helpers -│ ├── AsyncInfo/ # Async operation marshalling +│ ├── AsyncInfo/ # Async operation marshalling (Adapters/, Helpers/, TaskCompletionSources/) │ ├── Attributes/ # Internal attributes consumed by the interop stack │ ├── Bindables/ # XAML data-binding bridge types -│ ├── Buffers/ # IBuffer / Span marshalling helpers +│ ├── Buffers/ # IBuffer / Span marshalling helpers (MemoryStreams/) │ ├── Callbacks/ # ComWrappers callbacks │ ├── Collections/ # Collection adapters (IList↔IVector, IDictionary↔IMap, etc.) │ ├── Dispatching/ # DispatcherQueueSynchronizationContext and dispatcher integration @@ -197,25 +199,25 @@ WinRT.Runtime2/ │ ├── Extensions/ # Public extension methods on projected types │ ├── InteropDllImports/ # P/Invoke declarations │ ├── Marshalers/ # Type marshallers (string, delegate, value type, etc.) -│ ├── Marshalling/ # High-level marshalling APIs (WindowsRuntimeObjectMarshaller, etc.) +│ ├── Marshalling/ # High-level marshalling APIs (Collections/, SzArrays/) │ ├── ObjectReference/ # Native object lifetime (WindowsRuntimeObjectReference hierarchy) │ ├── Placeholders/ # Placeholder types for unresolved generic instantiations │ ├── Platform/ # Platform types (HRESULT, HSTRING, etc.) │ ├── ProjectionDllExports/ # Reserved DLL entry points consumed by the generated projection │ ├── ProjectionImpls/ # Built-in interface implementations (IStringable, IPropertyValue, etc.) -│ ├── Streams/ # IRandomAccessStream / Stream interop +│ ├── Streams/ # IRandomAccessStream / Stream interop (Adapters/, Operations/) │ ├── System.Runtime.InteropServices/ # Custom interop attributes added to the BCL surface │ ├── TypeMapGroups/ # Type mapping group markers for ComWrappers │ ├── TypeMapInfo/ # Type metadata caching │ ├── Vtables/ # COM vtable struct definitions │ └── WeakReferences/ # Weak reference support ├── NativeObjects/ # Managed wrappers for native Windows Runtime objects (collections, async, etc.) -├── Windows.Foundation/ # Manually projected foundation types -├── Windows.Storage.Streams/ # Manually projected stream types +├── Windows.Foundation/ # Manually projected foundation types (Collections/, Extensions/, Metadata/) +├── Windows.Storage.Streams/ # Manually projected stream types (Extensions/) ├── Windows.UI.Xaml.Interop/ # Manually projected XAML interop types ├── Xaml.Attributes/ # XAML-related attribute types ├── Properties/ # Exception messages and configuration (e.g. feature switches) -└── Exceptions/ # Exception types +└── Exceptions/ # Exception types (Microsoft.UI.Xaml/, Windows.UI.Xaml/) ``` **Key types:** @@ -311,7 +313,7 @@ A Roslyn incremental source generator and diagnostic analyzer package. Runs at * - **Target**: `net10.0`, C# 14, `IsRoslynComponent = true` - **Root namespace**: `WindowsRuntime.SourceGenerator` -- **Assembly name**: `WinRT.SourceGenerator2` +- **Assembly name**: `WinRT.SourceGenerator` - **Dependency**: `Microsoft.CodeAnalysis.CSharp` 5.0.0 **Three source generators:** @@ -358,6 +360,7 @@ The writer is consumed by other generators as a plain library reference — no i WinRT.Projection.Writer/ ├── ProjectionWriter.cs # Public Run(ProjectionWriterOptions) entry point ├── ProjectionWriterOptions.cs # Public options record +├── Attributes/ # Internal attributes consumed by the writer ├── Builders/ # Per-file emission orchestrators ├── Errors/ # WellKnownProjectionWriterException + Unhandled* (5xxx error IDs) ├── Extensions/ # AsmResolver / type-classifier extensions @@ -383,7 +386,7 @@ WinRT.Projection.Writer/ **Baseline emission** (`Resources/Base/`): always-emitted files that are not derived from `.winmd` metadata — `ComInteropExtensions.cs` (user-friendly extension methods wrapping internal interop interfaces), `InspectableVftbl.cs` (cached `IInspectable` vtable shape), `ReferenceInterfaceEntries.cs` (CCW interface entry table for the unknown-object fallback). -**Internal interop interfaces** (`WindowsRuntime.Internal.winmd`): a separately maintained `.winmd` of Windows SDK COM interop interfaces (e.g. `IDisplayInformationStaticsInterop`, `IPrintManagerInterop`) that are not included in standard SDK metadata. It is bundled in the CsWinRT NuGet package (`metadata/WindowsRuntime.Internal.winmd`) and added as additional input to the projection writer when building Windows SDK projections. Interfaces in this metadata carry the `[ProjectionInternal]` attribute, which causes all generated projection code for them to be emitted `internal`. The hand-written extension methods in `Resources/Base/ComInteropExtensions.cs` then surface user-friendly wrappers on the associated projected types (e.g. `DisplayInformation.GetForWindow(hwnd)`, `PrintManager.ShowPrintUIForWindowAsync(hwnd)`). +**Internal interop interfaces** (`WindowsRuntime.Internal.winmd`): a small set of Windows SDK COM interop interfaces (e.g. `IDisplayInformationStaticsInterop`, `IPrintManagerInterop`) that are not included in standard SDK metadata. The .winmd is produced from the C# `WinRT.Internal` project (see project 11 below); it is bundled in the CsWinRT NuGet package (`metadata/WindowsRuntime.Internal.winmd`) and added as additional input to the projection writer when building Windows SDK projections. Interfaces in this metadata carry the `[ProjectionInternal]` attribute, which causes all generated projection code for them to be emitted `internal`. The hand-written extension methods in `Resources/Base/ComInteropExtensions.cs` then surface user-friendly wrappers on the associated projected types (e.g. `DisplayInformation.GetForWindow(hwnd)`, `PrintManager.ShowPrintUIForWindowAsync(hwnd)`). ### 4. Reference projection generator (`src/WinRT.Projection.Ref.Generator/`) @@ -576,6 +579,30 @@ A build project (not a tool) used during **official CsWinRT builds** to produce - Output goes to a per-SDK-version subdirectory (`bin/{Configuration}/{WindowsSdkBuild}/`) - Built twice per SDK version: once for the base projection (`WinRT.Sdk.Projection.dll`) and once with `WindowsSdkXaml=true` for the XAML projection (`WinRT.Sdk.Xaml.Projection.dll`) +### 11. WinRT.Internal (`src/WinRT.Internal/`) + +A small build project that produces **`WindowsRuntime.Internal.winmd`** — the Windows SDK COM interop interface metadata bundled with the CsWinRT NuGet package and consumed by the projection writer when building Windows SDK projections (see "Internal interop interfaces" under the Projection writer section above). + +**Project settings:** + +- **Target**: `net10.0-windows10.0.26100.1` (the `.1` TFM revision selects the `cswinrt3` Windows SDK projection reference assemblies, which carry `[WindowsRuntimeMetadata]` attributes the WinMD generator reads) +- **Assembly name**: `WindowsRuntime.Internal` +- **Nullable**: `disable` (the Windows Runtime type system does not support nullability annotations) +- **WindowsSdkPackageVersion**: pinned (e.g. `10.0.26100.85-preview`) so the .NET SDK adds the implicit framework reference to the matching `Microsoft.Windows.SDK.NET.Ref` package +- **Disabled CsWinRT integration**: `CsWinRTEnabled = false`, `CsWinRTGenerateProjection = false`, `CsWinRTGenerateInteropAssembly[2] = false` (this project itself feeds back into the CsWinRT pipeline; no NuGet `Microsoft.Windows.CsWinRT` reference is involved) +- **`IsPackable = false`**: the produced `.dll` is just an intermediate artifact; only the `.winmd` is shipped +- References `WinRT.Runtime2` via `ProjectReference Private="false"` and depends on `WinRT.WinMD.Generator` for build ordering only (`ReferenceOutputAssembly="false"`) + +**Contents:** + +- **`HWND.cs`**: struct counterpart of the IDL `HWND` (custom-mapped to `nint` by the projection writer) +- **`ProjectionInternalAttribute.cs`**: forward declaration of the `[ProjectionInternal]` marker the projection writer reads to emit `internal` projections +- **14 `I*Interop.cs` files**: one per interop interface (`IAccountsSettingsPaneInterop`, `IDragDropManagerInterop`, `IInputPaneInterop`, `IPlayToManagerInterop`, `IPrintManagerInterop`, `IRadialControllerInterop`, `IRadialControllerConfigurationInterop`, `IRadialControllerIndependentInputSourceInterop`, `ISpatialInteractionManagerInterop`, `ISystemMediaTransportControlsInterop`, `IUIViewSettingsInterop`, `IUserConsentVerifierInterop`, `IWebAuthenticationCoreManagerInterop`, `IDisplayInformationStaticsInterop`) with their original IIDs and method signatures referencing Windows SDK projection types (e.g. `Windows.UI.ApplicationSettings.AccountsSettingsPane`) + +**MSBuild integration:** + +The project's own `GenerateWindowsRuntimeInternalWinMD` target runs after `CoreCompile` and invokes `cswinrtwinmdgen.exe` directly via `` (not via the `RunCsWinRTWinMDGenerator` MSBuild task) to avoid `MSB3027` file-lock contention from the persistent MSBuild build server keeping `WinRT.Generator.Tasks.dll` loaded. A response file in `$(IntermediateOutputPath)` is generated via `` and passed as `@`. The output `.winmd` is written to `$(TargetDir)$(AssemblyName).winmd` (i.e. `WindowsRuntime.Internal.winmd` next to the project's `.dll`), and `src/Directory.Build.props` points `$(CsWinRTInteropMetadata)` at that path so downstream consumers (notably `WinRT.Sdk.Projection`) pick it up. + --- ## NuGet package build pipeline (`nuget/`) @@ -618,6 +645,7 @@ The MSBuild integration is orchestrated through several `.props` and `.targets` - `WindowsRuntime.SourceGenerator` for the source generator - `WindowsRuntime.ProjectionWriter` for the projection writer library - `WindowsRuntime.ReferenceProjectionGenerator`, `WindowsRuntime.ProjectionGenerator`, `WindowsRuntime.ImplGenerator`, `WindowsRuntime.InteropGenerator`, `WindowsRuntime.WinMDGenerator` for build tools + - `WindowsRuntime.Internal` for the interop metadata authoring project (produces `WindowsRuntime.Internal.winmd`) - ABI types live under `ABI.{OriginalNamespace}` (e.g., `ABI.System.Collections.Generic`) - CLI tool assembly names are short: `cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `cswinrtimplgen`, `cswinrtinteropgen`, `cswinrtwinmdgen` - C# keywords in generated identifiers are escaped with `@` prefix @@ -688,7 +716,7 @@ Assembly-level `[TypeMapAssemblyTarget]` attributes (generated by the source gen | `src/Benchmarks/` | BenchmarkDotNet project for tracking performance of projection scenarios (e.g. async, events, QueryInterface, GUIDs). | | `src/Projections/` | Projects that generate and build projections from the Windows SDK, WinUI, and test metadata. **For local development and testing only** — these are not shipped in the NuGet package. | | `src/Samples/` | End-to-end sample projects: component authoring (`NetProjectionSample`, `AuthoringDemo`), WinUI desktop app (`WinUIDesktopSample`), background task component (`BgTaskComponent`). | -| `src/Tests/` | Test projects: unit tests (`UnitTest/`), functional/AOT tests (`FunctionalTests/`), source generator and analyzer tests (`SourceGenerator2Test/`), object lifetime tests (`ObjectLifetimeTests/`), authoring tests (`AuthoringTest/`), and the C++ test component (`TestComponentCSharp/`). | +| `src/Tests/` | Test projects: unit tests (`UnitTest/`), functional/AOT tests (`FunctionalTests/`), source generator and analyzer tests (`SourceGenerator2Test/`), object lifetime tests (`ObjectLifetimeTests.Lifted/`), authoring tests (`AuthoringTest/`, `AuthoringWuxTest/`, `AuthoringConsumptionTest/`, `AuthoringWuxConsumptionTest/`, `AuthoringWinUITest/`), build determinism (`BuildDeterminismTest/`), diagnostics (`DiagnosticTests/`), runtime framework version probing (`RuntimeFrameworkVersion/`), out-of-process EXE harness (`OOPExe/`), host (`HostTest/`), and the C++ test component (`TestComponentCSharp/`). | | `src/TestWinRT/` | Git submodule of [microsoft/TestWinRT](https://github.com/microsoft/TestWinRT/), providing general language projection test coverage. Produces `TestComponent` and `BenchmarkComponent` consumed by the unit test and benchmark projects. | | `build/` | Azure DevOps pipeline definitions for official builds and testing. Uses Maestro (from the [Arcade Build System](https://github.com/dotnet/arcade)) to publish builds for dependent projects. | | `eng/` | Engineering infrastructure: Maestro publishing helpers and shared build scripts. | diff --git a/.github/skills/update-copilot-instructions/SKILL.md b/.github/skills/update-copilot-instructions/SKILL.md index bf7b5cb0ea..a1ac8650df 100644 --- a/.github/skills/update-copilot-instructions/SKILL.md +++ b/.github/skills/update-copilot-instructions/SKILL.md @@ -19,7 +19,7 @@ Read `.github/copilot-instructions.md` in full. Take note of every factual claim ### Step 2: analyze each project in depth -Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in the instructions. For each project, verify: +Launch parallel explore agents for each of the 11 CsWinRT 3.0 projects listed in the instructions. For each project, verify: 1. **WinRT.Runtime (`src/WinRT.Runtime2/`)** - Directory structure matches what's documented @@ -33,9 +33,10 @@ Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in - Diagnostic analyzer list is complete and IDs are correct (check `DiagnosticDescriptors.cs` and `AnalyzerReleases.Shipped.md`) - Diagnostic ID range is accurate - Project dependencies are current + - Assembly name is current (it is `WinRT.SourceGenerator`, **not** `WinRT.SourceGenerator2` — the project folder has `2` for repo history, but the produced .dll does not) 3. **Projection writer (`src/WinRT.Projection.Writer/`)** - - Directory structure and namespaces match (`Builders/`, `Errors/`, `Factories/`, `Generation/`, `Helpers/`, `Metadata/`, `Models/`, `References/`, `Resolvers/`, `Resources/`, `Writers/`) + - Directory structure and namespaces match (`Attributes/`, `Builders/`, `Errors/`, `Extensions/`, `Factories/`, `Generation/`, `Helpers/`, `Metadata/`, `Models/`, `References/`, `Resolvers/`, `Resources/`, `Writers/`) - Public API surface (`ProjectionWriter.Run`, `ProjectionWriterOptions` shape) is accurate - Error ID range (5xxx in `Errors/WellKnownProjectionWriterExceptions.cs`) is accurate - Resources structure (`Additions/` per-namespace + `Base/` baseline) matches @@ -56,6 +57,7 @@ Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in - Namespace filter logic is current - Project settings and dependencies (project reference to `WinRT.Projection.Writer`) are current - The pipeline is documented as in-process (the projection writer is invoked as a library) + - `ProjectionGeneratorArgs` no longer contains any leftover `CsWinRTExePath` field 7. **Interop generator (`src/WinRT.Interop.Generator/`)** - Generated content categories are current @@ -71,13 +73,21 @@ Launch parallel explore agents for each of the 10 CsWinRT 3.0 projects listed in 9. **Generator tasks (`src/WinRT.Generator.Tasks/`)** - MSBuild task classes are accurately listed (including `RunCsWinRTProjectionRefGenerator` and `RunCsWinRTWinMDGenerator`) - Task-to-tool mappings are current + - No leftover `CsWinRTExePath` parameter on `RunCsWinRTMergedProjectionGenerator` 10. **SDK projection builds (`src/WinRT.Sdk.Projection/`)** - Assembly name logic (base vs XAML) is current - Windows SDK package download and WinMD sourcing is accurate - - Build parameters (`WindowsSdkBuild`, `WindowsSdkXaml`) are current + - Build parameters (`WindowsSdkBuild`, `WindowsSdkXaml`, `SdkPackageVersion`) are current - Project settings are current +11. **WinRT.Internal (`src/WinRT.Internal/`)** + - Hand-authored C# source files mirror the historical IDL interop interfaces (HWND struct, `[ProjectionInternal]` attribute, all 14 `I*Interop` interfaces with their original IIDs) + - Project TFM uses the CsWinRT 3.0 revision (`net10.0-windows10.0.X.1`) so the `cswinrt3` SDK projection reference assemblies are selected, and `WindowsSdkPackageVersion` is pinned to match + - CsWinRT integration is disabled on the project itself (`CsWinRTEnabled`, `CsWinRTGenerateProjection`, `CsWinRTGenerateInteropAssembly[2]` all `false`) + - `GenerateWindowsRuntimeInternalWinMD` target invokes `cswinrtwinmdgen.exe` directly via `` (not via the `UsingTask` mechanism) to avoid `MSB3027` file-lock contention in Visual Studio + - Output `.winmd` lands at `$(TargetDir)$(AssemblyName).winmd` (`WindowsRuntime.Internal.winmd`), and `src/Directory.Build.props` exposes it via `$(CsWinRTInteropMetadata)` for downstream consumers + ### Step 3: verify the test projects Verify the `src/Tests/` directory is accurately represented in the "Other directories" table. Check: From 9acaffa8c06a42f4efb11ad19e1fabe9fea659e1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 12 Jun 2026 14:52:17 -0700 Subject: [PATCH 7/9] Restore build-order dependency: Windows.csproj -> WinRT.Internal The CI build was failing on x86/x64 (green on arm64) with: CSWINRTPROJECTIONREFGEN0005: The input metadata path '...\src\WinRT.Internal\bin\Debug\net10.0-windows10.0.26100.1\WindowsRuntime.Internal.winmd' does not exist Root cause: 'Projections/Windows/Windows.csproj' (Microsoft.Windows.SDK.NET) includes the whole 'Windows' namespace ('-include Windows'), so its reference projection generation consumes 'WindowsRuntime.Internal.winmd' via the 'CsWinRTInteropMetadata' property. That .winmd is produced at build time by the 'WinRT.Internal' project (added in #2429), but no project had a build-order dependency on it. The ordering was previously provided *implicitly*: every projection project carried a ProjectReference to the slow-building C++ 'cswinrt/cswinrt.vcxproj', which delayed projection generation long enough for the fast C# 'WinRT.Internal' project to finish first. This PR removed those (genuinely dead) cswinrt.vcxproj references, exposing the latent race so the solution build could start Windows.csproj's projection generation before the internal .winmd existed. arm64 was unaffected because all the consuming projection projects are '' on ARM/ARM64 and never build there. Fix: add an explicit '' to the Windows.csproj entry in cswinrt.slnx, matching the existing pattern used for the generator-tool build dependencies. Windows.csproj is the only project that directly includes the bare 'Windows' namespace (verified across all of src/), and every other projection/test project that could be affected transitively ProjectReferences Windows.csproj, so they order correctly too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/cswinrt.slnx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index 16594b48c8..ece7da3d56 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -157,6 +157,14 @@ + + From bfdf6658ccb064a3ca9e3ac54bf69077c3cefa64 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 12 Jun 2026 14:52:57 -0700 Subject: [PATCH 8/9] Remove explanatory comment from solution dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/cswinrt.slnx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index ece7da3d56..e62de46e37 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -157,13 +157,6 @@ - From f3a91dc8a364792ae560f1f2227c828c16c182ac Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 12 Jun 2026 17:08:28 -0700 Subject: [PATCH 9/9] Set assembly name to WindowsRuntime.Internal Ensure the produced .dll/.winmd carry the expected assembly identity by setting AssemblyName and RootNamespace to WindowsRuntime.Internal in WinRT.Internal.csproj. Adds explanatory comment why the explicit name is required so the projection writer and other CsWinRT tools can locate WindowsRuntime.Internal.winmd (avoids defaulting to project name 'WinRT.Internal'). --- src/WinRT.Internal/WinRT.Internal.csproj | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Internal/WinRT.Internal.csproj b/src/WinRT.Internal/WinRT.Internal.csproj index 791fd59557..b8f65df35d 100644 --- a/src/WinRT.Internal/WinRT.Internal.csproj +++ b/src/WinRT.Internal/WinRT.Internal.csproj @@ -10,7 +10,18 @@ --> net10.0-windows10.0.26100.1 10.0.17763.0 - + + + WindowsRuntime.Internal + WindowsRuntime.Internal +