diff --git a/bindings/py/README.md b/bindings/py/README.md index 2a1cb72c..77835d7a 100644 --- a/bindings/py/README.md +++ b/bindings/py/README.md @@ -19,6 +19,45 @@ Generated package manifests pin `dynwinrt` to the exact version of Generated `IReference` values are projected as `T | None`; native values, `None`, and generated `IReference_*` wrappers are accepted as inputs. +### Nullability in type stubs + +WinRT metadata does not record which values can be null, and most APIs raise +an exception instead of returning null. The generated `.pyi` stubs therefore +type the values you receive as non-null by default: method and property +results, async results and out values. For example, +`StorageFolder.create_file_async()` returns `WinRTCoroutine[StorageFile]`. + +These values keep `| None`: + +- `IReference` values, projected as `T | None` everywhere; +- results of `Try*` members, such as `try_get_item_async()` or + `JsonObject.try_parse()`, where null means "not found"; +- results of Windows SDK members whose documentation says they can return + null, such as `Accelerometer.get_default()`, + `DispatcherQueue.get_for_current_thread()` or + `StorageFolder.get_parent_async()`. The codegen embeds this list, derived + from the Windows SDK API reference; it does not cover Windows App SDK + (`Microsoft.*`) APIs; +- `Object`/`IInspectable` values (`DynWinRTValue | None`) and delegate-typed + values, which are often null. + +Collection elements follow the collection holding them. Anyone can store null +in a mutable `IVector`, `IMap` or observable collection, so their elements, +item positions (`[index]`, iteration, `get_at()`, `lookup()`) and +`items()`/`values()` are typed `T | None`: a `JsonArray` holds +`IJsonValue | None`. Read-only views, iterators and arrays keep non-null +elements: `get_files_async()` returns `WinRTCoroutine[Sequence[StorageFile]]`. +A view, iterator or key-value pair obtained from a mutable collection, such as +the result of `get_view()` or `first()`, can still contain nulls although its +elements are typed non-null. + +Arguments keep accepting `None` where they did before. The stubs are +optimistic, like the generated TypeScript declarations: the runtime still +returns `None` when a WinRT API returns null, so check the API documentation +when a result can legitimately be absent. The inline annotations of the +generated `.py` modules, which `typing.get_type_hints()` and `--no-pyi` output +expose, still mark every object result `| None`. + ## Async WinRT operations Generated async methods return typed, asyncio-compatible operation objects: diff --git a/samples/python/async-file-io/app.py b/samples/python/async-file-io/app.py index 6cd7111d..83f015a7 100644 --- a/samples/python/async-file-io/app.py +++ b/samples/python/async-file-io/app.py @@ -15,12 +15,7 @@ async def run() -> None: with tempfile.TemporaryDirectory(prefix="dynwinrt-python-") as directory: with RoApartment(1), projected_lifetime_scope(): folder = await StorageFolder.get_folder_from_path_async(directory) - if folder is None: - raise RuntimeError("StorageFolder returned no temporary folder") - file = await folder.create_file_async("sample.txt") - if file is None: - raise RuntimeError("StorageFolder returned no file") await FileIO.write_text_async(file, "Hello from dynwinrt.") await FileIO.append_text_async( file, diff --git a/samples/python/cryptography/app.py b/samples/python/cryptography/app.py index e0a7c5c3..fbae2298 100644 --- a/samples/python/cryptography/app.py +++ b/samples/python/cryptography/app.py @@ -9,12 +9,8 @@ def sha256(text: str) -> str: with RoApartment(1), projected_lifetime_scope(): provider = HashAlgorithmProvider.open_algorithm("SHA256") - if provider is None: - raise RuntimeError("SHA256 provider is unavailable") data = IBuffer.from_bytes(text.encode("utf-8")) digest = provider.hash_data(data) - if digest is None: - raise RuntimeError("HashAlgorithmProvider returned no digest") copied_digest = digest.to_bytes() expected_length = provider.hash_length if len(copied_digest) != expected_length: diff --git a/samples/python/device-watcher/app.py b/samples/python/device-watcher/app.py index 83f5578e..f329c513 100644 --- a/samples/python/device-watcher/app.py +++ b/samples/python/device-watcher/app.py @@ -15,8 +15,6 @@ async def enumerate_devices(timeout: int, show_names: bool) -> None: with RoApartment(1), projected_lifetime_scope(): watcher = DeviceInformation.create_watcher() - if watcher is None: - raise RuntimeError("DeviceInformation returned no watcher") loop = asyncio.get_running_loop() enumeration_completed = asyncio.Event() diff --git a/samples/python/ocr-image/app.py b/samples/python/ocr-image/app.py index 6cd4c9cc..b5beb506 100644 --- a/samples/python/ocr-image/app.py +++ b/samples/python/ocr-image/app.py @@ -17,30 +17,21 @@ def normalized_words(value: str) -> set[str]: async def recognize(path: Path) -> str: with RoApartment(1), projected_lifetime_scope(): file = await StorageFile.get_file_from_path_async(str(path.resolve())) - if file is None: - raise RuntimeError("StorageFile returned no image file") stream = await file.open_read_async() - if stream is None: - raise RuntimeError("StorageFile returned no image stream") decoder = await BitmapDecoder.create_async( stream.as_interface(IRandomAccessStream) ) - if decoder is None: - raise RuntimeError("BitmapDecoder returned no decoder") bitmap = await decoder.get_software_bitmap_async() - if bitmap is None: - raise RuntimeError("BitmapDecoder returned no SoftwareBitmap") with bitmap: + # Try* members return None instead of raising when nothing matches. engine = OcrEngine.try_create_from_user_profile_languages() if engine is None: raise RuntimeError( "No OCR engine is available for the user profile languages" ) result = await engine.recognize_async(bitmap) - if result is None: - raise RuntimeError("OcrEngine returned no result") return result.text diff --git a/samples/python/text-to-speech/app.py b/samples/python/text-to-speech/app.py index c3267524..3321e080 100644 --- a/samples/python/text-to-speech/app.py +++ b/samples/python/text-to-speech/app.py @@ -13,8 +13,6 @@ async def speak(text: str, smoke: bool) -> None: with RoApartment(1), projected_lifetime_scope(): with SpeechSynthesizer() as synthesizer: stream = await synthesizer.synthesize_text_to_stream_async(text) - if stream is None: - raise RuntimeError("SpeechSynthesizer returned no stream") with stream: if smoke: diff --git a/tests/e2e/typecheck/python_generated_api.py b/tests/e2e/typecheck/python_generated_api.py index 3902d0c9..6230b2e6 100644 --- a/tests/e2e/typecheck/python_generated_api.py +++ b/tests/e2e/typecheck/python_generated_api.py @@ -3,7 +3,7 @@ import asyncio from collections.abc import Coroutine, Generator, Sequence -from typing import Any, Awaitable, List, Tuple +from typing import Any, Awaitable, List, Tuple, assert_type from dynwinrt import ( DynWinRTArray, @@ -22,7 +22,9 @@ IWwwFormUrlDecoderEntry, Uri, ) +from python_bindings.windows.foundation.collections import ValueSet from python_bindings.windows.globalization import Calendar +from python_bindings.windows.storage import IStorageItem, StorageFile, StorageFolder from python_bindings.windows.storage.streams import ( Buffer as WinRTBuffer, DataWriter, @@ -69,8 +71,9 @@ def check_uri() -> None: uri: Uri = Uri("https://example.com") relative: Uri = Uri("https://example.com/root/", "child") host: str = uri.host - combined: Uri | None = uri.combine_uri("child") - _: Tuple[str, Uri, Uri | None] = (host, relative, combined) + combined: Uri = uri.combine_uri("child") + absolute: str = combined.absolute_uri + _: Tuple[str, Uri, Uri, str] = (host, relative, combined, absolute) def check_nullable_value( @@ -86,8 +89,7 @@ def check_nullable_value( def check_string_vector(calendar: Calendar) -> None: - languages: Sequence[str] | None = calendar.languages - assert languages is not None + languages: Sequence[str] = calendar.languages first: str = languages[0] located: int = languages.index(first) many: List[str] = list(languages[:4]) @@ -152,3 +154,11 @@ def check_ibuffer_bytes() -> None: interface_bytes: bytes = interface_buffer.to_bytes() runtime_bytes: bytes = runtime_buffer.to_bytes() _: Tuple[bytes, bytes] = (interface_bytes, runtime_bytes) + + +async def check_output_nullability(folder: StorageFolder, values: ValueSet) -> None: + created: StorageFile = await folder.create_file_async("notes.txt") + names: List[str] = [item.name for item in await folder.get_files_async()] + assert_type(folder.try_get_item_async("notes.txt"), WinRTCoroutine[IStorageItem | None]) + assert_type(values["key"], DynWinRTValue | None) + _: Tuple[StorageFile, List[str]] = (created, names) diff --git a/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt b/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt new file mode 100644 index 00000000..912ee303 --- /dev/null +++ b/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt @@ -0,0 +1,17 @@ +# Reviewed corrections for windows-null-results.txt, applied last by +# scripts/extract-null-results.py. "+pattern" adds and "-pattern" removes +# members. A pattern is an api-id that may use fnmatch wildcards; it must match +# a member documented at the pinned winrt-api commit. + +# Sensors report a missing device through null across the whole family, as +# most of their pages state ("or null if no integrated ... are found"). ++M:Windows.Devices.Sensors.*.GetDefault* + +# Device lookups complete with null when the device is missing or access is +# denied, as many of their pages state. ++M:Windows.Devices.*.FromIdAsync(*) ++M:Windows.Devices.*.GetDefaultAsync* + +# Overridable implementation callbacks, not results that consumers receive. +-M:Windows.UI.Xaml.Automation.Peers.AutomationPeer.GetPatternCore(*) +-M:Windows.UI.Xaml.Controls.StyleSelector.SelectStyleCore(*) diff --git a/tools/dynwinrt-codegen/api-docs/windows-null-results.txt b/tools/dynwinrt-codegen/api-docs/windows-null-results.txt new file mode 100644 index 00000000..186c0eb5 --- /dev/null +++ b/tools/dynwinrt-codegen/api-docs/windows-null-results.txt @@ -0,0 +1,939 @@ +# Windows SDK members whose documented result can be null: a method's +# return value (for asynchronous methods, the completed result) or a +# property's value. dynwinrt-codegen keeps `| None` on these outputs. +# Source: https://github.com/MicrosoftDocs/winrt-api at commit 8448d5eecfbc2ed903f659f350841dcb4888bc8b +# Generated by scripts/extract-null-results.py; do not edit. Reviewed +# corrections belong in windows-null-results.overrides.txt. +M:Windows.AI.MachineLearning.TensorString.CreateReference +M:Windows.ApplicationModel.AppInstance.GetActivatedEventArgs +M:Windows.ApplicationModel.Calls.PhoneCall.GetFromId(System.String) +M:Windows.ApplicationModel.Calls.PhoneLineTransportDevice.FromId(System.String) +M:Windows.ApplicationModel.Contacts.ContactStore.GetContactListAsync(System.String) +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelData +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataAsync +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataType +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataTypeAsync +M:Windows.ApplicationModel.Package.GetContentGroupAsync(System.String) +M:Windows.ApplicationModel.UserDataAccounts.UserDataAccountManager.RequestStoreAsync(Windows.ApplicationModel.UserDataAccounts.UserDataAccountStoreAccessType) +M:Windows.ApplicationModel.UserDataTasks.UserDataTaskStore.GetListAsync(System.String) +M:Windows.ApplicationModel.Wallet.WalletItemStore.GetWalletItemAsync(System.String) +M:Windows.Data.Xml.Dom.DtdEntity.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.SelectNodesNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.DtdEntity.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.DtdEntity.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.DtdNotation.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.DtdNotation.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.IXmlNode.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNodeSelector.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.IXmlNodeSelector.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlAttribute.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlAttribute.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlCDataSection.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlCDataSection.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlComment.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlComment.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocument.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.GetElementById(System.String) +M:Windows.Data.Xml.Dom.XmlDocument.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocument.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocumentType.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocumentType.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlElement.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlElement.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlElement.SetAttributeNodeNS(Windows.Data.Xml.Dom.XmlAttribute) +M:Windows.Data.Xml.Dom.XmlEntityReference.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlEntityReference.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.GetNamedItem(System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.GetNamedItemNS(System.Object,System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.Item(System.UInt32) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.RemoveNamedItem(System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.RemoveNamedItemNS(System.Object,System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.SetNamedItem(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.SetNamedItemNS(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlNodeList.Item(System.UInt32) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlText.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlText.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Devices.Adc.AdcController.GetDefaultAsync +M:Windows.Devices.AllJoyn.AllJoynServiceInfo.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothAdapter.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothAdapter.GetDefaultAsync +M:Windows.Devices.Bluetooth.BluetoothDevice.FromBluetoothAddressAsync(System.UInt64) +M:Windows.Devices.Bluetooth.BluetoothDevice.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(System.UInt64) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(System.UInt64,Windows.Devices.Bluetooth.BluetoothAddressType) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.FromIdAsync(System.String,Windows.Devices.Bluetooth.GenericAttributeProfile.GattSharingMode) +M:Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService.FromIdAsync(System.String) +M:Windows.Devices.Custom.CustomDevice.FromIdAsync(System.String,Windows.Devices.Custom.DeviceAccessMode,Windows.Devices.Custom.DeviceSharingMode) +M:Windows.Devices.Display.Core.DisplayTarget.TryGetMonitor +M:Windows.Devices.Display.DisplayMonitor.FromIdAsync(System.String) +M:Windows.Devices.Gpio.GpioController.GetDefault +M:Windows.Devices.Gpio.GpioController.GetDefaultAsync +M:Windows.Devices.Haptics.InputHapticsManager.TryGetForThread(System.UInt32) +M:Windows.Devices.Haptics.VibrationDevice.FromIdAsync(System.String) +M:Windows.Devices.Haptics.VibrationDevice.GetDefaultAsync +M:Windows.Devices.HumanInterfaceDevice.HidDevice.FromIdAsync(System.String,Windows.Storage.FileAccessMode) +M:Windows.Devices.I2c.I2cController.GetDefaultAsync +M:Windows.Devices.I2c.I2cDevice.FromIdAsync(System.String,Windows.Devices.I2c.I2cConnectionSettings) +M:Windows.Devices.I2c.II2cDeviceStatics.FromIdAsync(System.String,Windows.Devices.I2c.I2cConnectionSettings) +M:Windows.Devices.Input.PenDevice.GetFromPointerId(System.UInt32) +M:Windows.Devices.Lights.Lamp.FromIdAsync(System.String) +M:Windows.Devices.Lights.Lamp.GetDefaultAsync +M:Windows.Devices.Lights.LampArray.FromIdAsync(System.String) +M:Windows.Devices.Midi.MidiInPort.FromIdAsync(System.String) +M:Windows.Devices.Midi.MidiOutPort.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionColorFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionColorFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionColorFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionColorFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionColorFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionDepthFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionDepthFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionDepthFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionDepthFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionDepthFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionInfraredFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.Provider.IPerceptionFrameProviderManager.GetFrameProvider(Windows.Devices.Perception.Provider.PerceptionFrameProviderInfo) +M:Windows.Devices.PointOfService.BarcodeScanner.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.BarcodeScanner.GetDefaultAsync +M:Windows.Devices.PointOfService.CashDrawer.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.CashDrawer.GetDefaultAsync +M:Windows.Devices.PointOfService.ClaimedLineDisplay.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.LineDisplay.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.LineDisplay.GetDefaultAsync +M:Windows.Devices.PointOfService.MagneticStripeReader.ClaimReaderAsync +M:Windows.Devices.PointOfService.MagneticStripeReader.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.MagneticStripeReader.GetDefaultAsync +M:Windows.Devices.PointOfService.PosPrinter.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.PosPrinter.GetDefaultAsync +M:Windows.Devices.Power.Battery.FromIdAsync(System.String) +M:Windows.Devices.Printers.Print3DDevice.FromIdAsync(System.String) +M:Windows.Devices.Pwm.PwmController.FromIdAsync(System.String) +M:Windows.Devices.Pwm.PwmController.GetDefaultAsync +M:Windows.Devices.Radios.Radio.FromIdAsync(System.String) +M:Windows.Devices.Scanners.ImageScanner.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Accelerometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Accelerometer.GetDefault +M:Windows.Devices.Sensors.Accelerometer.GetDefault(Windows.Devices.Sensors.AccelerometerReadingType) +M:Windows.Devices.Sensors.Accelerometer.GetDeviceSelector(Windows.Devices.Sensors.AccelerometerReadingType) +M:Windows.Devices.Sensors.ActivitySensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.ActivitySensor.GetDefaultAsync +M:Windows.Devices.Sensors.ActivitySensor.GetDeviceSelector +M:Windows.Devices.Sensors.Altimeter.GetDefault +M:Windows.Devices.Sensors.Barometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Barometer.GetDefault +M:Windows.Devices.Sensors.Barometer.GetDeviceSelector +M:Windows.Devices.Sensors.Compass.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Compass.GetDefault +M:Windows.Devices.Sensors.Compass.GetDeviceSelector +M:Windows.Devices.Sensors.Custom.CustomSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Custom.CustomSensor.GetDeviceSelector(System.Guid) +M:Windows.Devices.Sensors.Gyrometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Gyrometer.GetDefault +M:Windows.Devices.Sensors.Gyrometer.GetDeviceSelector +M:Windows.Devices.Sensors.HingeAngleSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync +M:Windows.Devices.Sensors.HingeAngleSensor.GetDeviceSelector +M:Windows.Devices.Sensors.HumanPresenceSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.HumanPresenceSensor.GetDefault +M:Windows.Devices.Sensors.HumanPresenceSensor.GetDefaultAsync +M:Windows.Devices.Sensors.Inclinometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Inclinometer.GetDefault +M:Windows.Devices.Sensors.Inclinometer.GetDefault(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.Inclinometer.GetDefaultForRelativeReadings +M:Windows.Devices.Sensors.Inclinometer.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.LightSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.LightSensor.GetDefault +M:Windows.Devices.Sensors.LightSensor.GetDeviceSelector +M:Windows.Devices.Sensors.Magnetometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Magnetometer.GetDefault +M:Windows.Devices.Sensors.Magnetometer.GetDeviceSelector +M:Windows.Devices.Sensors.OrientationSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.OrientationSensor.GetDefault +M:Windows.Devices.Sensors.OrientationSensor.GetDefault(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.OrientationSensor.GetDefault(Windows.Devices.Sensors.SensorReadingType,Windows.Devices.Sensors.SensorOptimizationGoal) +M:Windows.Devices.Sensors.OrientationSensor.GetDefaultForRelativeReadings +M:Windows.Devices.Sensors.OrientationSensor.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.OrientationSensor.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType,Windows.Devices.Sensors.SensorOptimizationGoal) +M:Windows.Devices.Sensors.Pedometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Pedometer.GetDefaultAsync +M:Windows.Devices.Sensors.Pedometer.GetDeviceSelector +M:Windows.Devices.Sensors.ProximitySensor.GetDeviceSelector +M:Windows.Devices.Sensors.SimpleOrientationSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.SimpleOrientationSensor.GetDefault +M:Windows.Devices.Sensors.SimpleOrientationSensor.GetDeviceSelector +M:Windows.Devices.SerialCommunication.SerialDevice.FromIdAsync(System.String) +M:Windows.Devices.SmartCards.SmartCardEmulator.GetDefaultAsync +M:Windows.Devices.SmartCards.SmartCardReader.FromIdAsync(System.String) +M:Windows.Devices.Sms.SmsDevice.FromIdAsync(System.String) +M:Windows.Devices.Sms.SmsDevice.GetDefaultAsync +M:Windows.Devices.Spi.ISpiDeviceStatics.FromIdAsync(System.String,Windows.Devices.Spi.SpiConnectionSettings) +M:Windows.Devices.Spi.SpiController.GetDefaultAsync +M:Windows.Devices.Spi.SpiDevice.FromIdAsync(System.String,Windows.Devices.Spi.SpiConnectionSettings) +M:Windows.Devices.Usb.UsbDevice.FromIdAsync(System.String) +M:Windows.Devices.WiFi.WiFiAdapter.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.Services.WiFiDirectService.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.WiFiDirectDevice.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.WiFiDirectDevice.FromIdAsync(System.String,Windows.Devices.WiFiDirect.WiFiDirectConnectionParameters) +M:Windows.Foundation.Diagnostics.FileLoggingSession.CloseAndSaveToFileAsync +M:Windows.Gaming.Input.Custom.GameControllerFactoryManager.TryGetFactoryControllerFromGameController(Windows.Gaming.Input.Custom.ICustomGameControllerFactory,Windows.Gaming.Input.IGameController) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.FromGameController(Windows.Gaming.Input.IGameController) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.FromGameControllerProvider(Windows.Gaming.Input.Custom.IGameControllerProvider) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.IsCopilot(Windows.System.User,System.String) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.IsPilot(Windows.System.User,System.String) +M:Windows.Gaming.UI.GameChatOverlay.GetDefault +M:Windows.Gaming.UI.GameMonitor.GetDefault +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseUInt(System.String) +M:Windows.Graphics.Capture.Direct3D11CaptureFramePool.TryGetNextFrame +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetCullingFrustum(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetViewTransform(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetVisibleFrustum(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicDisplay.TryGetViewConfiguration(Windows.Graphics.Holographic.HolographicViewConfigurationKind) +M:Windows.Graphics.Printing.Workflow.PrintWorkflowVirtualPrinterDataAvailableEventArgs.GetTargetFileAsync +M:Windows.Management.Deployment.PackageManager.FindPackage(System.String) +M:Windows.Management.Update.PreviewBuildsManager.GetDefault +M:Windows.Management.Update.WindowsUpdate.GetPropertyValue(System.String) +M:Windows.Media.Audio.AudioPlaybackConnection.TryCreateFromId(System.String) +M:Windows.Media.Capture.AppBroadcastStreamReader.TryGetNextAudioFrame +M:Windows.Media.Capture.AppBroadcastStreamReader.TryGetNextVideoFrame +M:Windows.Media.Capture.Frames.DepthMediaFrame.TryCreateCoordinateMapper(Windows.Media.Devices.Core.CameraIntrinsics,Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Media.Capture.Frames.MediaFrameSource.TryGetCameraIntrinsics(Windows.Media.Capture.Frames.MediaFrameFormat) +M:Windows.Media.Devices.CallControl.FromId(System.String) +M:Windows.Media.Devices.CallControl.GetDefault +M:Windows.Media.Ocr.OcrEngine.TryCreateFromLanguage(Windows.Globalization.Language) +M:Windows.Media.Ocr.OcrEngine.TryCreateFromUserProfileLanguages +M:Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile +M:Windows.Networking.NetworkOperators.ESimManager.TryCreateESimWatcher +M:Windows.Networking.Proximity.ProximityDevice.GetDefault +M:Windows.Networking.XboxLive.XboxLiveEndpointPair.FindEndpointPairByHostNamesAndPorts(Windows.Networking.HostName,System.String,Windows.Networking.HostName,System.String) +M:Windows.Networking.XboxLive.XboxLiveEndpointPair.FindEndpointPairBySocketAddressBytes(System.Byte[],System.Byte[]) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Foundation.Numerics.Vector3) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Foundation.Numerics.Vector3,Windows.Foundation.Numerics.Quaternion) +M:Windows.Perception.Spatial.SpatialCoordinateSystem.TryGetTransformTo(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.SpatialLocatorAttachedFrameOfReference.TryGetRelativeHeadingAtTimestamp(Windows.Perception.PerceptionTimestamp) +M:Windows.Perception.Spatial.SpatialStageFrameOfReference.TryGetMovementBounds(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.Surfaces.SpatialSurfaceInfo.TryComputeLatestMeshAsync(System.Double) +M:Windows.Perception.Spatial.Surfaces.SpatialSurfaceInfo.TryGetBounds(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Services.Maps.Guidance.GuidanceRoute.TryCreateFromMapRoute(Windows.Services.Maps.MapRoute) +M:Windows.Storage.FileProperties.StorageItemContentProperties.RetrievePropertiesAsync(Windows.Foundation.Collections.IIterable{System.String}) +M:Windows.Storage.IStorageItem2.GetParentAsync +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.TryGetItemAsync(System.String) +M:Windows.Storage.StorageLibrary.RequestAddFolderAsync +M:Windows.System.AppDiagnosticInfo.LaunchAsync +M:Windows.System.AppUriHandlerRegistrationManager.TryGetRegistration(System.String) +M:Windows.System.DispatcherQueue.GetForCurrentThread +M:Windows.System.RemoteSystems.RemoteSystem.FindByHostNameAsync(Windows.Networking.HostName) +M:Windows.System.User.GetFromId(System.String) +M:Windows.System.User.GetPictureAsync(Windows.System.UserPictureSize) +M:Windows.System.User.GetUserAgeRangeAsync +M:Windows.System.UserPicker.PickSingleUserAsync +M:Windows.System.UserProfile.UserInformation.GetAccountPicture(Windows.System.UserProfile.AccountPictureKind) +M:Windows.System.UserProfile.UserInformation.GetSessionInitiationProtocolUriAsync +M:Windows.UI.Composition.CompositionObject.TryGetAnimationController(System.String) +M:Windows.UI.Composition.Diagnostics.CompositionDebugSettings.TryGetSettings(Windows.UI.Composition.Compositor) +M:Windows.UI.Core.CoreWindow.GetForCurrentThread +M:Windows.UI.Core.CoreWindow.GetKeyState(Windows.System.VirtualKey) +M:Windows.UI.Input.Preview.Injection.InputInjector.TryCreate +M:Windows.UI.Input.Preview.Injection.InputInjector.TryCreateForAppBroadcastOnly +M:Windows.UI.Input.RadialControllerMenu.GetSelectedMenuItem +M:Windows.UI.Input.Spatial.SpatialHoldStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionController.TryGetBatteryReport +M:Windows.UI.Input.Spatial.SpatialInteractionController.TryGetRenderableModelAsync +M:Windows.UI.Input.Spatial.SpatialInteractionDetectedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSource.TryCreateHandMeshObserver +M:Windows.UI.Input.Spatial.SpatialInteractionSource.TryCreateHandMeshObserverAsync +M:Windows.UI.Input.Spatial.SpatialInteractionSourceProperties.TryGetLocation(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSourceProperties.TryGetSourceLossMitigationDirection(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSourceState.TryGetHandPose +M:Windows.UI.Input.Spatial.SpatialInteractionSourceState.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationCompletedEventArgs.TryGetCumulativeDelta(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationUpdatedEventArgs.TryGetCumulativeDelta(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialNavigationStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialPointerPose.TryGetAtTimestamp(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Perception.PerceptionTimestamp) +M:Windows.UI.Input.Spatial.SpatialPointerPose.TryGetInteractionSourcePose(Windows.UI.Input.Spatial.SpatialInteractionSource) +M:Windows.UI.Input.Spatial.SpatialRecognitionStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialTappedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Notifications.Management.UserNotificationListener.GetNotification(System.UInt32) +M:Windows.UI.Notifications.NotificationVisual.GetBinding(System.String) +M:Windows.UI.Popups.PopupMenu.ShowAsync(Windows.Foundation.Point) +M:Windows.UI.Popups.PopupMenu.ShowForSelectionAsync(Windows.Foundation.Rect) +M:Windows.UI.Popups.PopupMenu.ShowForSelectionAsync(Windows.Foundation.Rect,Windows.UI.Popups.Placement) +M:Windows.UI.Shell.FocusSessionManager.TryStartFocusSession +M:Windows.UI.Shell.FocusSessionManager.TryStartFocusSession(Windows.Foundation.DateTime) +M:Windows.UI.Text.ITextRange.InRange(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.ITextRange.InStory(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.ITextRange.MoveStart(Windows.UI.Text.TextRangeUnit,System.Int32) +M:Windows.UI.Text.RichEditTextRange.InRange(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.RichEditTextRange.InStory(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.RichEditTextRange.MoveStart(Windows.UI.Text.TextRangeUnit,System.Int32) +M:Windows.UI.UIAutomation.Core.AutomationRemoteOperationResult.GetOperand(Windows.UI.UIAutomation.Core.AutomationRemoteOperationOperandId) +M:Windows.UI.Xaml.Automation.Peers.AutomationPeer.GetPattern(Windows.UI.Xaml.Automation.Peers.PatternInterface) +M:Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.CreatePeerForElement(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.FromElement(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Automation.Peers.ItemsControlAutomationPeer.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Peers.LoopingSelectorAutomationPeer.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Provider.IDragProvider.GetGrabbedItems +M:Windows.UI.Xaml.Automation.Provider.IItemContainerProvider.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Provider.ITextRangeProvider.FindAttribute(System.Int32,System.Object,System.Boolean) +M:Windows.UI.Xaml.Automation.Provider.ITextRangeProvider.FindText(System.String,System.Boolean,System.Boolean) +M:Windows.UI.Xaml.Controls.Control.GetTemplateChild(System.String) +M:Windows.UI.Xaml.Controls.DataTemplateSelector.GetElement(Windows.UI.Xaml.ElementFactoryGetArgs) +M:Windows.UI.Xaml.Controls.DatePickerFlyoutItem.GetIndexedProperty(System.String,Windows.UI.Xaml.Interop.TypeName) +M:Windows.UI.Xaml.Controls.IItemContainerMapping.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.IItemContainerMapping.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.InkToolbar.GetToolButton(Windows.UI.Xaml.Controls.InkToolbarTool) +M:Windows.UI.Xaml.Controls.ItemContainerGenerator.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.ItemContainerGenerator.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.ItemsControl.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.ItemsControl.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.ItemsControl.GetItemsOwner(Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.ItemsControl.ItemsControlFromItemContainer(Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.Maps.MapControl.GetVisibleRegion(Windows.UI.Xaml.Controls.Maps.MapVisibleRegionKind) +M:Windows.UI.Xaml.Controls.Maps.StreetsidePanorama.FindNearbyAsync(Windows.Devices.Geolocation.Geopoint) +M:Windows.UI.Xaml.Controls.Maps.StreetsidePanorama.FindNearbyAsync(Windows.Devices.Geolocation.Geopoint,System.Double) +M:Windows.UI.Xaml.Controls.NavigationView.ContainerFromMenuItem(System.Object) +M:Windows.UI.Xaml.Controls.StyleSelector.SelectStyle(System.Object,Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.SwapChainPanel.CreateCoreIndependentInputSource(Windows.UI.Core.CoreInputDeviceTypes) +M:Windows.UI.Xaml.Controls.TreeView.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.TreeView.ContainerFromNode(Windows.UI.Xaml.Controls.TreeViewNode) +M:Windows.UI.Xaml.Data.ICustomPropertyProvider.GetCustomProperty(System.String) +M:Windows.UI.Xaml.Data.ICustomPropertyProvider.GetIndexedProperty(System.String,Windows.UI.Xaml.Interop.TypeName) +M:Windows.UI.Xaml.DataTemplate.GetElement(Windows.UI.Xaml.ElementFactoryGetArgs) +M:Windows.UI.Xaml.Documents.TextElement.FindName(System.String) +M:Windows.UI.Xaml.Documents.TextPointer.GetPositionAtOffset(System.Int32,Windows.UI.Xaml.Documents.LogicalDirection) +M:Windows.UI.Xaml.FrameworkElement.FindName(System.String) +M:Windows.UI.Xaml.FrameworkElement.GetBindingExpression(Windows.UI.Xaml.DependencyProperty) +M:Windows.UI.Xaml.Input.FocusManager.FindNextFocusableElement(Windows.UI.Xaml.Input.FocusNavigationDirection) +M:Windows.UI.Xaml.Input.FocusManager.FindNextFocusableElement(Windows.UI.Xaml.Input.FocusNavigationDirection,Windows.Foundation.Rect) +M:Windows.UI.Xaml.Input.FocusManager.GetFocusedElement +M:Windows.UI.Xaml.Input.PointerRoutedEventArgs.GetIntermediatePoints(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Markup.IXamlType.GetMember(System.String) +M:Windows.UI.Xaml.Media.Animation.ConnectedAnimationService.GetAnimation(System.String) +M:Windows.UI.Xaml.Media.Animation.Storyboard.GetCurrentTime +M:Windows.Web.IUriToStreamResolver.UriToStreamAsync(Windows.Foundation.Uri) +P:Windows.ApplicationModel.Activation.AppointmentsProviderAddAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderRemoveAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderReplaceAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderShowAppointmentDetailsActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderShowTimeFrameActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.CachedFileUpdaterActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.DeviceActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.DeviceActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.DialReceiverActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.NeighboringFilesQuery +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.FileOpenPickerActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileOpenPickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.FileSavePickerActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileSavePickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.FolderPickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.IViewSwitcherProvider.ViewSwitcher +P:Windows.ApplicationModel.Activation.LaunchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.LockScreenCallActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.Data +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ProtocolForResultsActivatedEventArgs.Data +P:Windows.ApplicationModel.Activation.ProtocolForResultsActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.RestrictedLaunchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.SearchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.SearchActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ShareTargetActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.VoiceCommandActivatedEventArgs.User +P:Windows.ApplicationModel.AppInstallerInfo.PausedUntil +P:Windows.ApplicationModel.AppInstance.RecommendedInstance +P:Windows.ApplicationModel.Appointments.Appointment.Reminder +P:Windows.ApplicationModel.Background.BackgroundTaskRegistration.Trigger +P:Windows.ApplicationModel.Background.BluetoothLEAdvertisementPublisherTrigger.PreferredTransmitPowerLevelInDBm +P:Windows.ApplicationModel.Background.IBackgroundTaskRegistration2.Trigger +P:Windows.ApplicationModel.Background.RfcommConnectionTrigger.InboundConnection +P:Windows.ApplicationModel.Background.RfcommConnectionTrigger.OutboundConnection +P:Windows.ApplicationModel.Calls.PhoneLineDialResult.DialedCall +P:Windows.ApplicationModel.Package.MachineExternalLocation +P:Windows.ApplicationModel.Package.UserExternalLocation +P:Windows.ApplicationModel.PackageCatalogAddOptionalPackageResult.ExtendedError +P:Windows.ApplicationModel.PackageCatalogAddOptionalPackageResult.Package +P:Windows.ApplicationModel.PackageId.ResourceId +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.DetailText +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.Image +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.ImageAlternateText +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.Tag +P:Windows.ApplicationModel.UserDataAccounts.UserDataAccount.EnterpriseId +P:Windows.ApplicationModel.UserDataAccounts.UserDataAccount.Icon +P:Windows.ApplicationModel.UserDataTasks.UserDataTask.Reminder +P:Windows.ApplicationModel.Wallet.WalletItem.ExpirationDate +P:Windows.ApplicationModel.Wallet.WalletItem.LastUpdated +P:Windows.ApplicationModel.Wallet.WalletItem.RelevantDate +P:Windows.ApplicationModel.Wallet.WalletTransaction.TransactionDate +P:Windows.Data.Xml.Dom.DtdEntity.FirstChild +P:Windows.Data.Xml.Dom.DtdEntity.LastChild +P:Windows.Data.Xml.Dom.DtdEntity.NextSibling +P:Windows.Data.Xml.Dom.DtdEntity.NodeValue +P:Windows.Data.Xml.Dom.DtdNotation.FirstChild +P:Windows.Data.Xml.Dom.DtdNotation.LastChild +P:Windows.Data.Xml.Dom.DtdNotation.NextSibling +P:Windows.Data.Xml.Dom.DtdNotation.NodeValue +P:Windows.Data.Xml.Dom.IXmlNode.Attributes +P:Windows.Data.Xml.Dom.IXmlNode.FirstChild +P:Windows.Data.Xml.Dom.IXmlNode.LastChild +P:Windows.Data.Xml.Dom.XmlAttribute.FirstChild +P:Windows.Data.Xml.Dom.XmlAttribute.LastChild +P:Windows.Data.Xml.Dom.XmlAttribute.NextSibling +P:Windows.Data.Xml.Dom.XmlCDataSection.ChildNodes +P:Windows.Data.Xml.Dom.XmlCDataSection.FirstChild +P:Windows.Data.Xml.Dom.XmlCDataSection.LastChild +P:Windows.Data.Xml.Dom.XmlComment.ChildNodes +P:Windows.Data.Xml.Dom.XmlComment.FirstChild +P:Windows.Data.Xml.Dom.XmlComment.LastChild +P:Windows.Data.Xml.Dom.XmlDocument.FirstChild +P:Windows.Data.Xml.Dom.XmlDocument.LastChild +P:Windows.Data.Xml.Dom.XmlDocument.ParentNode +P:Windows.Data.Xml.Dom.XmlDocumentFragment.Attributes +P:Windows.Data.Xml.Dom.XmlDocumentFragment.FirstChild +P:Windows.Data.Xml.Dom.XmlDocumentFragment.LastChild +P:Windows.Data.Xml.Dom.XmlDocumentType.Attributes +P:Windows.Data.Xml.Dom.XmlDocumentType.FirstChild +P:Windows.Data.Xml.Dom.XmlDocumentType.LastChild +P:Windows.Data.Xml.Dom.XmlElement.Attributes +P:Windows.Data.Xml.Dom.XmlElement.FirstChild +P:Windows.Data.Xml.Dom.XmlElement.LastChild +P:Windows.Data.Xml.Dom.XmlEntityReference.Attributes +P:Windows.Data.Xml.Dom.XmlEntityReference.FirstChild +P:Windows.Data.Xml.Dom.XmlEntityReference.LastChild +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.Attributes +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.FirstChild +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.LastChild +P:Windows.Data.Xml.Dom.XmlText.Attributes +P:Windows.Data.Xml.Dom.XmlText.FirstChild +P:Windows.Data.Xml.Dom.XmlText.LastChild +P:Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementPublisher.PreferredTransmitPowerLevelInDBm +P:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.ParentServices +P:Windows.Devices.Display.Core.DisplayTarget.DeviceInterfacePath +P:Windows.Devices.Display.DisplayMonitor.BluePrimary +P:Windows.Devices.Display.DisplayMonitor.DeviceId +P:Windows.Devices.Display.DisplayMonitor.DisplayAdapterDeviceId +P:Windows.Devices.Display.DisplayMonitor.DisplayAdapterId +P:Windows.Devices.Display.DisplayMonitor.DisplayName +P:Windows.Devices.Display.DisplayMonitor.GreenPrimary +P:Windows.Devices.Display.DisplayMonitor.PhysicalSizeInInches +P:Windows.Devices.Display.DisplayMonitor.RedPrimary +P:Windows.Devices.Display.DisplayMonitor.WhitePoint +P:Windows.Devices.Enumeration.DeviceInformation.EnclosureLocation +P:Windows.Devices.Geolocation.GeocoordinateSatelliteData.GeometricDilutionOfPrecision +P:Windows.Devices.Geolocation.GeocoordinateSatelliteData.TimeDilutionOfPrecision +P:Windows.Devices.Geolocation.Geoposition.CivicAddress +P:Windows.Devices.Geolocation.Geoposition.VenueData +P:Windows.Devices.Haptics.InputHapticsManager.CurrentHapticsController +P:Windows.Devices.PointOfService.BarcodeScannerReport.ScanDataLabel +P:Windows.Devices.PointOfService.CashDrawer.DrawerEventSource +P:Windows.Devices.PointOfService.ClaimedLineDisplay.CustomGlyphs +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Journal +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Receipt +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Slip +P:Windows.Devices.Scanners.ImageScanner.AutoConfiguration +P:Windows.Devices.Scanners.ImageScanner.FeederConfiguration +P:Windows.Devices.Scanners.ImageScanner.FlatbedConfiguration +P:Windows.Devices.Sensors.AccelerometerReading.PerformanceCount +P:Windows.Devices.Sensors.AltimeterReading.PerformanceCount +P:Windows.Devices.Sensors.BarometerReading.PerformanceCount +P:Windows.Devices.Sensors.CompassReading.PerformanceCount +P:Windows.Devices.Sensors.Custom.CustomSensorReading.PerformanceCount +P:Windows.Devices.Sensors.GyrometerReading.PerformanceCount +P:Windows.Devices.Sensors.InclinometerReading.PerformanceCount +P:Windows.Devices.Sensors.LightSensorReading.PerformanceCount +P:Windows.Devices.Sensors.MagnetometerReading.PerformanceCount +P:Windows.Devices.Sensors.OrientationSensorReading.PerformanceCount +P:Windows.Devices.SmartCards.SmartCardCryptogramPlacementStep.Algorithm +P:Windows.Devices.Usb.UsbBulkInEndpointDescriptor.Pipe +P:Windows.Devices.WiFi.WiFiOnDemandHotspotNetworkProperties.RemainingBatteryPercent +P:Windows.Devices.WiFiDirect.Services.WiFiDirectServiceAutoAcceptSessionConnectedEventArgs.SessionInfo +P:Windows.Devices.WiFiDirect.WiFiDirectAdvertisement.InformationElements +P:Windows.Gaming.Input.RacingWheel.WheelMotor +P:Windows.Globalization.Fonts.LanguageFontGroup.DocumentAlternate1Font +P:Windows.Globalization.Fonts.LanguageFontGroup.DocumentAlternate2Font +P:Windows.Globalization.Fonts.LanguageFontGroup.FixedWidthTextFont +P:Windows.Graphics.Display.DisplayEnhancementOverride.BrightnessOverrideSettings +P:Windows.Graphics.Display.DisplayEnhancementOverride.ColorOverrideSettings +P:Windows.Graphics.Display.DisplayInformation.DiagonalSizeInInches +P:Windows.Media.Audio.AudioFileInputNode.EndTime +P:Windows.Media.Audio.AudioFileInputNode.StartTime +P:Windows.Media.Audio.AudioGraphSettings.PrimaryRenderDevice +P:Windows.Media.Audio.MediaSourceAudioInputNode.EndTime +P:Windows.Media.Audio.MediaSourceAudioInputNode.StartTime +P:Windows.Media.Capture.AdvancedCapturedPhoto.FrameBoundsRelativeToReferencePhoto +P:Windows.Media.Capture.Frames.MediaFrameFormat.VideoFormat +P:Windows.Media.Capture.Frames.MediaFrameReference.AudioMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameReference.BufferMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameReference.VideoMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameSourceGetPropertyResult.Value +P:Windows.Media.Capture.Frames.VideoMediaFrame.SoftwareBitmap +P:Windows.Media.Capture.Frames.VideoMediaFrameFormat.DepthFormat +P:Windows.Media.Capture.MediaCaptureInitializationSettings.AudioDeviceId +P:Windows.Media.Capture.MediaCaptureInitializationSettings.VideoDeviceId +P:Windows.Media.Capture.MediaCaptureSettings.AudioDeviceId +P:Windows.Media.Capture.MediaCaptureSettings.VideoDeviceId +P:Windows.Media.Core.MediaSource.AdaptiveMediaSource +P:Windows.Media.Core.MediaSource.MediaStreamSource +P:Windows.Media.Core.MediaSource.MseStreamSource +P:Windows.Media.Core.MediaStreamSourceSampleRequest.Sample +P:Windows.Media.Core.MediaStreamSourceStartingRequest.StartPosition +P:Windows.Media.Devices.VideoDeviceControllerGetDevicePropertyResult.Value +P:Windows.Media.Effects.AudioEffect.AcousticEchoCancellationConfiguration +P:Windows.Media.MediaProperties.MediaEncodingProfile.Audio +P:Windows.Media.MediaProperties.MediaEncodingProfile.Video +P:Windows.Media.Ocr.OcrResult.TextAngle +P:Windows.Media.Protection.PlayReady.PlayReadyContentHeader.HeaderWithEmbeddedUpdates +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.ChallengeCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.ResponseCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.Uri +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.ChallengeCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.ResponseCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.Uri +P:Windows.Media.Protection.PlayReady.PlayReadyStatics.HardwareDRMDisabledAtTime +P:Windows.Media.Protection.PlayReady.PlayReadyStatics.HardwareDRMDisabledUntilTime +P:Windows.Media.SpeechRecognition.SpeechRecognitionResult.Constraint +P:Windows.Media.SpeechRecognition.SpeechRecognizer.SystemSpeechLanguage +P:Windows.Media.Streaming.Adaptive.AdaptiveMediaSourceCorrelatedTimes.PresentationTimeStamp +P:Windows.Media.VideoFrame.Direct3DSurface +P:Windows.Media.VideoFrame.SoftwareBitmap +P:Windows.Networking.BackgroundTransfer.BackgroundDownloader.CompletionGroup +P:Windows.Networking.BackgroundTransfer.BackgroundUploader.CompletionGroup +P:Windows.Networking.EndpointPair.LocalHostName +P:Windows.Networking.HostName.IPInformation +P:Windows.Networking.NetworkOperators.ESim.SlotIndex +P:Windows.Networking.NetworkOperators.ESimDownloadProfileMetadataResult.ProfileMetadata +P:Windows.Networking.NetworkOperators.NetworkOperatorNotificationEventDetails.SmsMessage +P:Windows.Networking.NetworkOperators.UssdReply.Message +P:Windows.Networking.Proximity.TriggeredConnectionStateChangedEventArgs.Socket +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.BadgeNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.RawNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.TileNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.ToastNotification +P:Windows.Networking.Sockets.IWebSocketInformation.Protocol +P:Windows.Networking.Sockets.MessageWebSocketInformation.Protocol +P:Windows.Networking.Sockets.StreamSocketInformation.RemoteAddress +P:Windows.Networking.Sockets.StreamSocketInformation.SessionKey +P:Windows.Networking.Sockets.StreamWebSocketInformation.Protocol +P:Windows.Networking.Vpn.VpnChannel.CurrentRequestTransportContext +P:Windows.Perception.People.EyesPose.Gaze +P:Windows.Perception.Spatial.SpatialStageFrameOfReference.Current +P:Windows.Perception.Spatial.Surfaces.SpatialSurfaceMesh.VertexNormals +P:Windows.Security.EnterpriseData.ProtectionPolicyManager.PrimaryManagedIdentity +P:Windows.Services.Cortana.CortanaActionableInsights.User +P:Windows.Services.Cortana.CortanaActionableInsightsOptions.ContentSourceWebLink +P:Windows.Services.Cortana.CortanaActionableInsightsOptions.SurroundingText +P:Windows.Services.Maps.Guidance.GuidanceManeuver.RoadSignpost +P:Windows.Services.Maps.Guidance.GuidanceRoadSignpost.Exit +P:Windows.Services.Maps.Guidance.GuidanceRoadSignpost.ExitNumber +P:Windows.Services.Maps.MapRouteFinderResult.AlternateRoutes +P:Windows.Services.Store.StoreContext.User +P:Windows.Services.Store.StoreSku.SubscriptionInfo +P:Windows.System.RemoteSystems.RemoteSystemSessionCreationResult.Session +P:Windows.System.RemoteSystems.RemoteSystemSessionJoinResult.Session +P:Windows.UI.Composition.CompositionGeometricClip.Geometry +P:Windows.UI.Composition.RedirectVisual.Source +P:Windows.UI.Composition.ScalarNaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.ScalarNaturalMotionAnimation.InitialValue +P:Windows.UI.Composition.Vector2NaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.Vector2NaturalMotionAnimation.InitialValue +P:Windows.UI.Composition.Vector3NaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.Vector3NaturalMotionAnimation.InitialValue +P:Windows.UI.Input.Inking.InkPresenter.StrokeContainer +P:Windows.UI.Input.PointerPointProperties.ZDistance +P:Windows.UI.Input.RadialControllerButtonClickedEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonHoldingEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonPressedEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonReleasedEventArgs.Contact +P:Windows.UI.Input.RadialControllerControlAcquiredEventArgs.Contact +P:Windows.UI.Input.RadialControllerRotationChangedEventArgs.Contact +P:Windows.UI.Input.RadialControllerScreenContactContinuedEventArgs.Contact +P:Windows.UI.Input.RadialControllerScreenContactStartedEventArgs.Contact +P:Windows.UI.Input.Spatial.SpatialInteractionController.SimpleHapticsController +P:Windows.UI.Input.Spatial.SpatialInteractionSource.Controller +P:Windows.UI.Input.Spatial.SpatialInteractionSourceLocation.SourcePointerPose +P:Windows.UI.Input.Spatial.SpatialInteractionSourceState.ControllerProperties +P:Windows.UI.Input.Spatial.SpatialPointerPose.Eyes +P:Windows.UI.Popups.UICommand.Invoked +P:Windows.UI.Shell.Tasks.AppTaskInfo.EndTime +P:Windows.UI.StartScreen.TileMixedRealityModel.BoundingBox +P:Windows.UI.Text.ContentLinkInfo.Uri +P:Windows.UI.Text.Core.CoreTextSelectionRequest.Selection +P:Windows.UI.Text.Core.CoreTextTextRequest.Text +P:Windows.UI.WebUI.WebUIAppointmentsProviderAddAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderRemoveAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderReplaceAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderShowAppointmentDetailsActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderShowTimeFrameActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIBackgroundTaskInstance.Current +P:Windows.UI.WebUI.WebUICachedFileUpdaterActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIContactPanelActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDeviceActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDevicePairingActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDialReceiverActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileOpenPickerActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileOpenPickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUIFileSavePickerActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileSavePickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUIFolderPickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUILaunchActivatedEventArgs.User +P:Windows.UI.WebUI.WebUILockScreenActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIProtocolActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIProtocolForResultsActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIRestrictedLaunchActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIShareTargetActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIToastNotificationActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIVoiceCommandActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIWebAccountProviderActivatedEventArgs.User +P:Windows.UI.Xaml.Automation.Provider.IDragProvider.DropEffect +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.LargeChange +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Maximum +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Minimum +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.SmallChange +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Value +P:Windows.UI.Xaml.BringIntoViewOptions.TargetRect +P:Windows.UI.Xaml.Controls.AnchorRequestedEventArgs.Anchor +P:Windows.UI.Xaml.Controls.AppBarButton.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.AppBarToggleButton.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.AutoSuggestBox.Description +P:Windows.UI.Xaml.Controls.AutoSuggestBox.QueryIcon +P:Windows.UI.Xaml.Controls.BitmapIcon.UriSource +P:Windows.UI.Xaml.Controls.BitmapIconSource.UriSource +P:Windows.UI.Xaml.Controls.Border.Background +P:Windows.UI.Xaml.Controls.Border.BackgroundTransition +P:Windows.UI.Xaml.Controls.Border.BorderBrush +P:Windows.UI.Xaml.Controls.Button.Flyout +P:Windows.UI.Xaml.Controls.CalendarDatePicker.Description +P:Windows.UI.Xaml.Controls.CalendarDatePicker.Header +P:Windows.UI.Xaml.Controls.CalendarDatePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.CalendarViewDayItemChangingEventArgs.Item +P:Windows.UI.Xaml.Controls.ColorPicker.PreviousColor +P:Windows.UI.Xaml.Controls.ComboBox.Description +P:Windows.UI.Xaml.Controls.ComboBox.Header +P:Windows.UI.Xaml.Controls.ComboBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.CommandBar.CommandBarOverflowPresenterStyle +P:Windows.UI.Xaml.Controls.ContainerContentChangingEventArgs.Item +P:Windows.UI.Xaml.Controls.ContentControl.Content +P:Windows.UI.Xaml.Controls.ContentControl.ContentTemplateRoot +P:Windows.UI.Xaml.Controls.ContentDialog.CloseButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.CloseButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonText +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonText +P:Windows.UI.Xaml.Controls.ContentPresenter.Background +P:Windows.UI.Xaml.Controls.ContentPresenter.BackgroundTransition +P:Windows.UI.Xaml.Controls.ContentPresenter.BorderBrush +P:Windows.UI.Xaml.Controls.ContentPresenter.Content +P:Windows.UI.Xaml.Controls.ContentPresenter.ContentTemplate +P:Windows.UI.Xaml.Controls.ContentPresenter.Foreground +P:Windows.UI.Xaml.Controls.Control.Background +P:Windows.UI.Xaml.Controls.Control.BorderBrush +P:Windows.UI.Xaml.Controls.DatePicker.Header +P:Windows.UI.Xaml.Controls.DatePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.DatePicker.SelectedDate +P:Windows.UI.Xaml.Controls.Flyout.FlyoutPresenterStyle +P:Windows.UI.Xaml.Controls.Grid.BorderBrush +P:Windows.UI.Xaml.Controls.GroupStyle.ContainerStyle +P:Windows.UI.Xaml.Controls.GroupStyle.ContainerStyleSelector +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderContainerStyle +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderTemplate +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderTemplateSelector +P:Windows.UI.Xaml.Controls.HandwritingView.PlacementTarget +P:Windows.UI.Xaml.Controls.Hub.Header +P:Windows.UI.Xaml.Controls.Hub.HeaderTemplate +P:Windows.UI.Xaml.Controls.Hub.SemanticZoomOwner +P:Windows.UI.Xaml.Controls.HubSection.Header +P:Windows.UI.Xaml.Controls.HubSection.HeaderTemplate +P:Windows.UI.Xaml.Controls.IScrollAnchorProvider.CurrentAnchor +P:Windows.UI.Xaml.Controls.IconElement.Foreground +P:Windows.UI.Xaml.Controls.IconSource.Foreground +P:Windows.UI.Xaml.Controls.IconSourceElement.IconSource +P:Windows.UI.Xaml.Controls.InkToolbarCustomPenButton.ConfigurationContent +P:Windows.UI.Xaml.Controls.InkToolbarCustomToolButton.ConfigurationContent +P:Windows.UI.Xaml.Controls.ItemsControl.ItemContainerStyle +P:Windows.UI.Xaml.Controls.ItemsControl.ItemTemplate +P:Windows.UI.Xaml.Controls.ItemsControl.Items +P:Windows.UI.Xaml.Controls.ItemsControl.ItemsPanelRoot +P:Windows.UI.Xaml.Controls.ItemsControl.ItemsSource +P:Windows.UI.Xaml.Controls.ItemsPresenter.Footer +P:Windows.UI.Xaml.Controls.ItemsPresenter.FooterTemplate +P:Windows.UI.Xaml.Controls.ItemsPresenter.Header +P:Windows.UI.Xaml.Controls.ItemsPresenter.HeaderTemplate +P:Windows.UI.Xaml.Controls.ListPickerFlyout.ItemTemplate +P:Windows.UI.Xaml.Controls.ListPickerFlyout.ItemsSource +P:Windows.UI.Xaml.Controls.ListPickerFlyout.SelectedItem +P:Windows.UI.Xaml.Controls.ListPickerFlyout.SelectedValue +P:Windows.UI.Xaml.Controls.ListViewBase.Footer +P:Windows.UI.Xaml.Controls.ListViewBase.FooterTemplate +P:Windows.UI.Xaml.Controls.ListViewBase.Header +P:Windows.UI.Xaml.Controls.ListViewBase.HeaderTemplate +P:Windows.UI.Xaml.Controls.ListViewBase.SemanticZoomOwner +P:Windows.UI.Xaml.Controls.Maps.MapControl.Region +P:Windows.UI.Xaml.Controls.MediaElement.AudioStreamIndex +P:Windows.UI.Xaml.Controls.MediaElement.Source +P:Windows.UI.Xaml.Controls.MediaPlayerElement.Source +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.Command +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.CommandParameter +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.MenuFlyoutSubItem.Items +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemContainerStyle +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemTemplate +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemsSource +P:Windows.UI.Xaml.Controls.NavigationView.PaneFooter +P:Windows.UI.Xaml.Controls.NavigationView.PaneHeader +P:Windows.UI.Xaml.Controls.NavigationView.PaneToggleButtonStyle +P:Windows.UI.Xaml.Controls.NavigationView.SelectedItem +P:Windows.UI.Xaml.Controls.NavigationViewItem.Icon +P:Windows.UI.Xaml.Controls.Page.BottomAppBar +P:Windows.UI.Xaml.Controls.Page.TopAppBar +P:Windows.UI.Xaml.Controls.Panel.Background +P:Windows.UI.Xaml.Controls.Panel.BackgroundTransition +P:Windows.UI.Xaml.Controls.ParallaxView.Child +P:Windows.UI.Xaml.Controls.PasswordBox.Description +P:Windows.UI.Xaml.Controls.PasswordBox.Header +P:Windows.UI.Xaml.Controls.PasswordBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.PasswordBox.InputScope +P:Windows.UI.Xaml.Controls.PasswordBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.PasswordBox.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.Pivot.LeftHeader +P:Windows.UI.Xaml.Controls.Pivot.RightHeader +P:Windows.UI.Xaml.Controls.Primitives.ButtonBase.Command +P:Windows.UI.Xaml.Controls.Primitives.ButtonBase.CommandParameter +P:Windows.UI.Xaml.Controls.Primitives.FlyoutBase.XamlRoot +P:Windows.UI.Xaml.Controls.Primitives.Selector.SelectedItem +P:Windows.UI.Xaml.Controls.Primitives.Selector.SelectedValue +P:Windows.UI.Xaml.Controls.RadioButton.GroupName +P:Windows.UI.Xaml.Controls.RatingControl.PlaceholderValue +P:Windows.UI.Xaml.Controls.RatingControl.Value +P:Windows.UI.Xaml.Controls.RelativePanel.BorderBrush +P:Windows.UI.Xaml.Controls.RichEditBox.Description +P:Windows.UI.Xaml.Controls.RichEditBox.Header +P:Windows.UI.Xaml.Controls.RichEditBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.RichEditBox.InputScope +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionHighlightColorWhenNotFocused +P:Windows.UI.Xaml.Controls.RichTextBlock.Foreground +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionEnd +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionFlyout +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionStart +P:Windows.UI.Xaml.Controls.ScrollViewer.CurrentAnchor +P:Windows.UI.Xaml.Controls.SettingsFlyout.HeaderBackground +P:Windows.UI.Xaml.Controls.SettingsFlyout.HeaderForeground +P:Windows.UI.Xaml.Controls.SettingsFlyout.IconSource +P:Windows.UI.Xaml.Controls.Slider.Header +P:Windows.UI.Xaml.Controls.Slider.HeaderTemplate +P:Windows.UI.Xaml.Controls.SplitButton.Command +P:Windows.UI.Xaml.Controls.SplitButton.CommandParameter +P:Windows.UI.Xaml.Controls.SplitButton.Flyout +P:Windows.UI.Xaml.Controls.SplitView.Content +P:Windows.UI.Xaml.Controls.SplitView.Pane +P:Windows.UI.Xaml.Controls.StackPanel.BorderBrush +P:Windows.UI.Xaml.Controls.SwipeItem.Command +P:Windows.UI.Xaml.Controls.SwipeItem.CommandParameter +P:Windows.UI.Xaml.Controls.SwipeItem.IconSource +P:Windows.UI.Xaml.Controls.TextBlock.Foreground +P:Windows.UI.Xaml.Controls.TextBlock.SelectionEnd +P:Windows.UI.Xaml.Controls.TextBlock.SelectionFlyout +P:Windows.UI.Xaml.Controls.TextBlock.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.TextBlock.SelectionStart +P:Windows.UI.Xaml.Controls.TextBox.Description +P:Windows.UI.Xaml.Controls.TextBox.Header +P:Windows.UI.Xaml.Controls.TextBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.TextBox.InputScope +P:Windows.UI.Xaml.Controls.TextBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.TextBox.SelectionHighlightColorWhenNotFocused +P:Windows.UI.Xaml.Controls.TimePicker.Header +P:Windows.UI.Xaml.Controls.TimePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.TimePicker.SelectedTime +P:Windows.UI.Xaml.Controls.ToolTip.PlacementRect +P:Windows.UI.Xaml.Controls.ToolTip.PlacementTarget +P:Windows.UI.Xaml.Controls.TreeView.ItemContainerStyle +P:Windows.UI.Xaml.Controls.TreeView.ItemTemplate +P:Windows.UI.Xaml.Controls.TreeView.ItemsSource +P:Windows.UI.Xaml.Controls.TreeViewItem.ItemsSource +P:Windows.UI.Xaml.Data.Binding.ConverterParameter +P:Windows.UI.Xaml.Data.Binding.RelativeSource +P:Windows.UI.Xaml.Data.Binding.TargetNullValue +P:Windows.UI.Xaml.Data.ICollectionView.CurrentItem +P:Windows.UI.Xaml.Documents.Glyphs.Fill +P:Windows.UI.Xaml.Documents.Glyphs.FontUri +P:Windows.UI.Xaml.Documents.Hyperlink.NavigateUri +P:Windows.UI.Xaml.Documents.TextElement.XamlRoot +P:Windows.UI.Xaml.DragEventArgs.DragUIOverride +P:Windows.UI.Xaml.ElementFactoryGetArgs.Parent +P:Windows.UI.Xaml.ElementFactoryRecycleArgs.Parent +P:Windows.UI.Xaml.FrameworkElement.Parent +P:Windows.UI.Xaml.FrameworkElement.Style +P:Windows.UI.Xaml.Input.FindNextElementOptions.SearchRoot +P:Windows.UI.Xaml.Input.FocusManagerGotFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.FocusManagerLostFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.GettingFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.KeyboardAccelerator.ScopeOwner +P:Windows.UI.Xaml.Input.LosingFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Markup.IXamlType.ContentProperty +P:Windows.UI.Xaml.Markup.IXamlType.ItemType +P:Windows.UI.Xaml.Markup.IXamlType.KeyType +P:Windows.UI.Xaml.Media.Animation.BeginStoryboard.Storyboard +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.By +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.From +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.To +P:Windows.UI.Xaml.Media.Animation.ColorKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.By +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.From +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.To +P:Windows.UI.Xaml.Media.Animation.DoubleKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.ObjectKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.ObjectKeyFrame.Value +P:Windows.UI.Xaml.Media.Animation.PointAnimation.By +P:Windows.UI.Xaml.Media.Animation.PointAnimation.EasingFunction +P:Windows.UI.Xaml.Media.Animation.PointAnimation.From +P:Windows.UI.Xaml.Media.Animation.PointAnimation.To +P:Windows.UI.Xaml.Media.Animation.PointKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Brush.RelativeTransform +P:Windows.UI.Xaml.Media.GeneralTransform.Inverse +P:Windows.UI.Xaml.Media.PlaneProjection.ProjectionMatrix +P:Windows.UI.Xaml.Media.RectangleGeometry.Rect +P:Windows.UI.Xaml.Media.TimelineMarker.Time +P:Windows.UI.Xaml.Navigation.NavigationEventArgs.Parameter +P:Windows.UI.Xaml.Setter.Property +P:Windows.UI.Xaml.Shapes.Polygon.Points +P:Windows.UI.Xaml.Shapes.Polyline.Points +P:Windows.UI.Xaml.Shapes.Shape.Fill +P:Windows.UI.Xaml.Shapes.Shape.Stroke +P:Windows.UI.Xaml.Style.BasedOn +P:Windows.UI.Xaml.UIElement.CacheMode +P:Windows.UI.Xaml.UIElement.Clip +P:Windows.UI.Xaml.UIElement.ContextFlyout +P:Windows.UI.Xaml.UIElement.RenderTransform +P:Windows.UI.Xaml.UIElement.Transform3D +P:Windows.UI.Xaml.UIElement.XamlRoot +P:Windows.UI.Xaml.VisualStateGroup.CurrentState +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentDisposition +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentLength +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentLocation +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentMD5 +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentRange +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentType +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.Expires +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.LastModified +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Authorization +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Date +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Host +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.IfModifiedSince +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.IfUnmodifiedSince +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.MaxForwards +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.ProxyAuthorization +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Referer +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Age +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Date +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Location +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.RetryAfter +P:Windows.Web.Http.HttpCookie.Expires +P:Windows.Web.Http.HttpGetInputStreamResult.ExtendedError +P:Windows.Web.Http.HttpGetStringResult.ExtendedError +P:Windows.Web.Http.HttpRequestResult.ExtendedError +P:Windows.Web.Syndication.SyndicationFeed.FirstUri +P:Windows.Web.Syndication.SyndicationFeed.IconUri +P:Windows.Web.Syndication.SyndicationFeed.LastUri +P:Windows.Web.Syndication.SyndicationFeed.NextUri +P:Windows.Web.Syndication.SyndicationFeed.PreviousUri +P:Windows.Web.Syndication.SyndicationItem.EditMediaUri +P:Windows.Web.Syndication.SyndicationItem.EditUri diff --git a/tools/dynwinrt-codegen/scripts/extract-null-results.py b/tools/dynwinrt-codegen/scripts/extract-null-results.py new file mode 100644 index 00000000..987a305d --- /dev/null +++ b/tools/dynwinrt-codegen/scripts/extract-null-results.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +"""Extract the Windows SDK members whose documented result can be null. + +WinRT metadata carries no nullability, so dynwinrt-codegen types most outputs +as non-null and keeps `| None` for members whose documentation says that the +result can be null. This script derives that list from MicrosoftDocs/winrt-api +at a pinned commit and writes only doc comment IDs (api-ids), never +documentation text, to api-docs/windows-null-results.txt. + +A method (`M:`) or property (`P:`) is listed when +- a sentence of its `## -returns` or `## -property-value` section says that + the result can be null (for an asynchronous method: its completed result), or +- a sentence of its `## -remarks` section says that the member itself + ("this method", "this property", "it", or the member's name) returns or is + null. +Sentences that negate null, that describe null arguments, or that describe an +object holding a null value are ignored, and so are Boolean results, attached +properties, constructors and members of generic types. Reviewed corrections +from api-docs/windows-null-results.overrides.txt are applied last. + +The documentation is read from git objects, without a working tree: + + python extract-null-results.py # fetch the pinned commit + python extract-null-results.py --repo DIR # reuse a local clone + python extract-null-results.py --commit SHA # move the pin +""" + +from __future__ import annotations + +import argparse +import fnmatch +import re +import subprocess +import sys +import tempfile +from collections import Counter +from collections.abc import Iterator +from pathlib import Path + +REPOSITORY = "https://github.com/MicrosoftDocs/winrt-api" +COMMIT = "8448d5eecfbc2ed903f659f350841dcb4888bc8b" +CODEGEN = Path(__file__).resolve().parent.parent +OUTPUT = CODEGEN / "api-docs" / "windows-null-results.txt" +OVERRIDES = CODEGEN / "api-docs" / "windows-null-results.overrides.txt" + +NULL = re.compile(r"\bnull(?:ptr)?\b(?![- ](?:terminat|character|char\b))", re.I) +NEGATION = re.compile( + r"\b(?:never|not|cannot|can't|won't|doesn't|does not|isn't|is not|will not|must not|may not)" + r"\s+(?:be\s+|is\s+|return\s+|returns\s+)?(?:a\s+)?null\b|\bnon-?null\b", + re.I, +) +ARGUMENT = re.compile( + r"\bnull\s+(?:was|is|were|are)\s+passed\b" + r"|\bpass(?:es|ed|ing)?\s+(?:in\s+)?(?:a\s+)?null\b" + r"|\bother than\s+null\b" + r"|\b(?:argument|parameter)\s+(?:is|was|are)\s+null\b" + r"|\bnull\s+(?:for|as)\s+(?:the\s+)?\w+\s+(?:argument|parameter)\b" + r"|\bexception\b[^.]*\bset\s+to\s+null\b|\bset\s+to\s+null\b[^.]*\bexception\b", + re.I, +) +HOLDER = re.compile( + r"\b(?:with|holds?|holding|contains?|containing|supports?|has|have)\s+(?:a|an)\s+" + r"(?:json\s+)?null\s+value\b", + re.I, +) +BOOLEAN = re.compile(r"\A\W*(?:true|false)\b", re.I) +REMARKS_GAP = r"(?:(?!\b(?:when|if|unless|called|until|whether|and|but)\b)[^.;,]){0,40}?" +REMARKS_VERB = ( + r"\b(?:returns?|is|will\s+be|may\s+be|can\s+be|could\s+be|might\s+be|is\s+set\s+to)" + r"\s+(?:a\s+|an\s+|the\s+)?null(?:ptr)?\b" +) + + +def git(repo: Path, *args: str) -> str: + return subprocess.run( + ["git", "-C", str(repo), *args], check=True, capture_output=True, text=True + ).stdout + + +def ensure_commit(repo: Path, commit: str) -> None: + if not (repo / ".git").exists(): + repo.mkdir(parents=True, exist_ok=True) + git(repo, "init", "--quiet") + present = subprocess.run( + ["git", "-C", str(repo), "cat-file", "-e", f"{commit}^{{commit}}"], + capture_output=True, + ) + if present.returncode != 0: + git(repo, "fetch", "--quiet", "--depth", "1", REPOSITORY, commit) + + +def documents(repo: Path, commit: str) -> Iterator[str]: + listing = git(repo, "ls-tree", "-r", "-z", commit) + blobs = [] + for entry in listing.split("\0"): + if not entry: + continue + info, path = entry.split("\t", 1) + _, kind, sha = info.split() + if kind == "blob" and path.endswith(".md"): + blobs.append(sha) + with subprocess.Popen( + ["git", "-C", str(repo), "cat-file", "--batch"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as batch: + assert batch.stdin is not None and batch.stdout is not None + for sha in blobs: + batch.stdin.write(sha.encode() + b"\n") + batch.stdin.flush() + size = int(batch.stdout.readline().split()[2]) + data = batch.stdout.read(size) + batch.stdout.read(1) + yield data.decode("utf-8", "replace").replace("\r\n", "\n") + batch.stdin.close() + + +def front_matter(text: str) -> dict[str, str]: + match = re.match(r"\A---[ \t]*\n(.*?)\n---", text, re.S) + fields = {} + for line in match.group(1).splitlines() if match else []: + key, _, value = line.strip().partition(":") + if key.startswith("-"): + fields[key[1:]] = value.strip() + return fields + + +def section(text: str, name: str) -> str: + match = re.search(rf"^## -{re.escape(name)}[ \t]*\n(.*?)(?=^## -|\Z)", text, re.M | re.S) + return plain(match.group(1)) if match else "" + + +def plain(markdown: str) -> str: + text = re.sub(r"", " ", markdown, flags=re.S) + text = re.sub(r"!?\[([^\]]*)\]\([^)]*\)", r"\1", text) + text = re.sub(r"\[!(?:NOTE|IMPORTANT|TIP|WARNING|CAUTION)\]", " ", text) + text = re.sub(r"(?m)^\s*>\s?", " ", text) + text = re.sub(r"[*_`]", "", text) + return " ".join(text.split()) + + +def sentences(text: str) -> list[str]: + return [sentence for sentence in re.split(r"(?<=[.!?])\s+", text) if NULL.search(sentence)] + + +def states_null(sentence: str) -> bool: + return not (NEGATION.search(sentence) or ARGUMENT.search(sentence) or HOLDER.search(sentence)) + + +def result_is_nullable(result: str) -> bool: + if not result or BOOLEAN.match(result): + return False + return any(states_null(sentence) for sentence in sentences(result)) + + +def remarks_say_null(remarks: str, member: str) -> bool: + subject = ( + r"(?:\bthis\s+(?:method|property|function|call|operation)" + r"|\bthe\s+(?:method|property|call|operation)" + rf"|\bit|\b{re.escape(member)})\b" + ) + claim = re.compile(subject + REMARKS_GAP + REMARKS_VERB, re.I) + returns = re.compile(rf"\bif\s+(?:this|it|{re.escape(member)})\s+returns\s+null\b", re.I) + return any( + (claim.search(sentence) or returns.search(sentence)) and states_null(sentence) + for sentence in sentences(remarks) + ) + + +def member_name(api_id: str) -> str: + return api_id[2:].split("(", 1)[0].rsplit(".", 1)[-1] + + +def extract(repo: Path, commit: str) -> tuple[set[str], set[str], Counter[str]]: + documented: set[str] = set() + nullable: set[str] = set() + sources: Counter[str] = Counter() + for text in documents(repo, commit): + fields = front_matter(text) + api_id = fields.get("api-id", "") + api_type = fields.get("api-type", "") + if not api_id.startswith(("M:", "P:")) or api_type in { + "winrt attachedproperty", + "winrt constructor", + }: + continue + if "#ctor" in api_id or "`" in api_id.split("(", 1)[0]: + continue + documented.add(api_id) + result = section(text, "returns" if api_id.startswith("M:") else "property-value") + if result_is_nullable(result): + sources["returns" if api_id.startswith("M:") else "property-value"] += 1 + nullable.add(api_id) + elif remarks_say_null(section(text, "remarks"), member_name(api_id)): + sources["remarks"] += 1 + nullable.add(api_id) + return documented, nullable, sources + + +def apply_overrides(documented: set[str], nullable: set[str], sources: Counter[str]) -> set[str]: + result = set(nullable) + for number, raw in enumerate(OVERRIDES.read_text(encoding="utf-8").splitlines(), 1): + entry = raw.split("#", 1)[0].strip() + if not entry: + continue + sign, pattern = entry[0], entry[1:].strip() + if sign not in "+-" or not pattern: + sys.exit(f"{OVERRIDES.name}:{number}: expected '+api-id' or '-api-id'") + matches = {api_id for api_id in documented if fnmatch.fnmatchcase(api_id, pattern)} + if not matches: + sys.exit(f"{OVERRIDES.name}:{number}: {pattern} matches no documented member") + if sign == "+": + sources["override additions"] += len(matches - result) + result |= matches + else: + sources["override removals"] += len(matches & result) + result -= matches + return result + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__.split("\n\n", 1)[0]) + parser.add_argument("--commit", default=COMMIT, help="winrt-api commit to read") + parser.add_argument("--repo", type=Path, help="local winrt-api clone to reuse") + arguments = parser.parse_args() + + with tempfile.TemporaryDirectory(prefix="winrt-api-") as scratch: + repo = arguments.repo or Path(scratch) + ensure_commit(repo, arguments.commit) + documented, nullable, sources = extract(repo, arguments.commit) + members = sorted(apply_overrides(documented, nullable, sources)) + + header = [ + "# Windows SDK members whose documented result can be null: a method's", + "# return value (for asynchronous methods, the completed result) or a", + "# property's value. dynwinrt-codegen keeps `| None` on these outputs.", + f"# Source: {REPOSITORY} at commit {arguments.commit}", + "# Generated by scripts/extract-null-results.py; do not edit. Reviewed", + "# corrections belong in windows-null-results.overrides.txt.", + ] + OUTPUT.write_text("\n".join(header + members) + "\n", encoding="utf-8", newline="\n") + print(f"{len(members)} members from {len(documented)} documented methods and properties") + for source, count in sources.most_common(): + print(f" {source}: {count}") + + +if __name__ == "__main__": + main() diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs index 3cb9aea5..52484e00 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs @@ -10,13 +10,14 @@ use crate::codegen::winrt::shared::imports::{ }; use super::naming::{PythonProjectionContext, PythonTypeIdentity, to_snake_case}; +use super::nullability::AnnotationSurface; use super::signature::{ py_convert_return, py_runtime_named_symbol, py_runtime_symbol, py_type_guard, py_wrap_arg, py_wrap_async, py_wrap_async_with_converters, }; use super::type_helpers::{ method_pydoc, py_delegate_callable_type, py_factory_return_type, py_method_abi_output_count, - py_method_outputs, py_method_return_type, py_output_type, py_param_list, + py_method_outputs, py_method_return_type, py_param_list, py_property_type, }; fn is_delegate_type(typ: &TypeMeta, context: &PythonProjectionContext) -> bool { @@ -282,7 +283,12 @@ fn generate_factory_method_invoke_named( let in_params = get_in_params(method); let py_params = py_param_list(&in_params, context); - let return_py_type = py_factory_return_type(&context.class_name(class), method, context); + let return_py_type = py_factory_return_type( + &context.class_name(class), + method, + AnnotationSurface::Runtime, + context, + ); let mut out = String::new(); let method_name = name_override @@ -360,7 +366,7 @@ fn generate_static_method_invoke_named( let in_params = get_in_params(method); let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Runtime, context); let mut out = String::new(); let iface_symbol = context.reference_name(&iface.type_identity()); @@ -780,7 +786,7 @@ pub(crate) fn generate_method_body( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_output_type(typ, context)) + .map(|typ| py_property_type(method, typ, AnnotationSurface::Runtime, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(" @_property\n"); out.push_str(&format!(" def {}(self) -> {}:\n", prop_name, py_return)); @@ -830,7 +836,7 @@ pub(crate) fn generate_method_body( )); } else { let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Runtime, context); let method_name = name_override .map(|s| s.to_string()) .unwrap_or_else(|| to_snake_case(&method.name)); diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs index 84a04006..d2194c4e 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs @@ -8,6 +8,7 @@ mod implementation; pub(crate) mod method; pub(crate) mod naming; mod native_types; +pub(crate) mod nullability; pub(crate) mod overloads; mod shared; pub(crate) mod signature; diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs new file mode 100644 index 00000000..6a9de1fc --- /dev/null +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs @@ -0,0 +1,464 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The one policy deciding whether a projected output annotation admits +//! `None`. +//! +//! WinRT metadata carries no nullability, so whether a consumer-facing value +//! is annotated `T | None` depends on the value's type, the position it is +//! received at, facts about the member producing it, and the generated file +//! the annotation is rendered into. Renderers in `type_helpers` spell the +//! non-null type expression; only [`output_admits_none`] decides whether +//! `| None` is appended. + +use crate::codegen::winrt::shared::imports::ireference_inner_type; +use crate::meta::{ElementAccess, MethodMeta}; +use crate::types::TypeMeta; + +use super::collections::CollectionKind; +use super::naming::PythonProjectionContext; + +/// The generated artifact an annotation is rendered into. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum AnnotationSurface { + /// Inline annotations of runtime `.py` modules. `typing.get_type_hints()` + /// exposes them, and checkers read them when stubs are not generated. + Runtime, + /// `.pyi` stubs read by type checkers. + Stub, +} + +/// Where the consumer receives a value from the projection. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum OutputPosition { + /// Return value of a method. + Return, + /// Out value in a method's result tuple. + OutParam, + /// Value read from a property getter. + Property, + /// Completed value of an async operation. + AsyncResult, + /// Progress value reported by an async operation. + AsyncProgress, + /// Element, key or value read from a collection or an array. + CollectionElement, + /// Sender or argument the runtime passes to a consumer callback. + CallbackParam, + /// Instance created by an activation factory. Activation reports failure + /// by raising, so the instance is never null unless the factory follows + /// the `Try*` pattern. + Activation, +} + +/// The collection holding an element. Whether a reference-type element admits +/// `None` depends only on this; see [`element_admits_none`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum ElementContainer { + /// `IIterable`, `IIterator`, `IVectorView`, `IMapView` or `IKeyValuePair`. + View, + /// `IVector`, `IMap` or their observable forms. + Mutable, + /// An array returned by a member that does not read collection elements. + Array, +} + +impl ElementContainer { + pub(crate) fn of(kind: CollectionKind) -> Self { + match kind { + CollectionKind::MutableSequence | CollectionKind::MutableMapping => Self::Mutable, + CollectionKind::Iterable + | CollectionKind::Iterator + | CollectionKind::Sequence + | CollectionKind::Mapping + | CollectionKind::KeyValuePair => Self::View, + } + } +} + +impl From for ElementContainer { + fn from(access: ElementAccess) -> Self { + match access { + ElementAccess::ReadOnly => Self::View, + ElementAccess::Mutable => Self::Mutable, + } + } +} + +/// The collection element rule: anyone can store null in a mutable collection, +/// so its reference-type elements admit `None`. Views, iterators and arrays +/// are typed like other outputs. Positions that read elements inherit the +/// rule of their owning collection; a view obtained from a mutable collection +/// follows the view rule. +pub(crate) fn element_admits_none(container: ElementContainer) -> bool { + match container { + ElementContainer::Mutable => true, + ElementContainer::View | ElementContainer::Array => false, + } +} + +/// A position plus the facts about the member producing the value. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct OutputSite { + pub(crate) position: OutputPosition, + /// The member follows the `Try*` pattern, so a null result is part of + /// its contract ("not found", "could not parse"). + pub(crate) try_method: bool, + /// The Windows SDK documentation says the member's result can be null. + pub(crate) documented_null: bool, + /// The collection holding the value: set on collection elements, and on + /// the results of members that read elements of the collection declaring + /// them (`get_at`, `lookup`, `current`, ...). + pub(crate) container: Option, +} + +impl OutputSite { + pub(crate) fn of(position: OutputPosition) -> Self { + Self { + position, + try_method: false, + documented_null: false, + container: None, + } + } + + pub(crate) fn for_method(method: &MethodMeta, position: OutputPosition) -> Self { + Self { + position, + try_method: is_try_method(method), + documented_null: method.documented_null_result, + container: method.element_access.map(ElementContainer::from), + } + } + + /// An element read from a collection of the given kind. + pub(crate) fn element_of(container: ElementContainer) -> Self { + Self::of(OutputPosition::CollectionElement).element_in(container) + } + + /// The site of a value nested in this one, such as an async result. + /// Member facts carry over; the policy decides where they apply. + pub(crate) fn nested(self, position: OutputPosition) -> Self { + Self { position, ..self } + } + + /// The site of an element held by `container`, nested in this value. + pub(crate) fn element_in(self, container: ElementContainer) -> Self { + Self { + position: OutputPosition::CollectionElement, + container: Some(container), + ..self + } + } +} + +/// `TryParse`, `TryGetItemAsync`, ...: the CLR name is `Try` followed by an +/// uppercase letter. +pub(crate) fn is_try_method(method: &MethodMeta) -> bool { + method + .raw_name + .strip_prefix("Try") + .and_then(|rest| rest.chars().next()) + .is_some_and(|next| next.is_ascii_uppercase()) +} + +/// Whether the runtime converts a null ABI value of `typ` to `None`: +/// interface pointers (objects, classes, interfaces, delegates and +/// parameterized interfaces, including `IReference`). Value types, +/// strings, arrays and async operations never project as `None`. +pub(crate) fn may_project_none(typ: &TypeMeta) -> bool { + matches!( + typ, + TypeMeta::Object + | TypeMeta::Delegate { .. } + | TypeMeta::RuntimeClass { .. } + | TypeMeta::Interface { .. } + | TypeMeta::Parameterized { .. } + ) +} + +/// Whether the annotation of `typ` received at `site` admits `None` on +/// `surface`. This is the only place that makes that decision for outputs. +pub(crate) fn output_admits_none( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> bool { + if ireference_inner_type(typ).is_some() { + return true; + } + if !may_project_none(typ) { + return false; + } + match surface { + // Runtime annotations stay pessimistic: `typing.get_type_hints()` and + // `--no-pyi` consumers see every reference output as optional. + AnnotationSurface::Runtime => site.position != OutputPosition::Activation, + AnnotationSurface::Stub => stub_output_admits_none(typ, site, context), + } +} + +/// Stubs are optimistic, like the JavaScript declarations: WinRT metadata has +/// no nullability, and most APIs raise instead of returning null. +fn stub_output_admits_none( + typ: &TypeMeta, + site: OutputSite, + context: &PythonProjectionContext, +) -> bool { + use OutputPosition::{ + Activation, AsyncResult, CallbackParam, CollectionElement, OutParam, Property, Return, + }; + + // `Object` positions are frequently null, e.g. the arguments of a + // `TypedEventHandler`. + if matches!(typ, TypeMeta::Object) { + return true; + } + // Delegate-typed values are raw handles that are null while unset. + if context.is_delegate_type(typ) { + return site.position != CallbackParam; + } + // `Try*` members report "not found" through a null result, and the + // Windows SDK documentation names the other members that return null. + let member_result = matches!( + site.position, + Return | OutParam | Property | AsyncResult | Activation + ); + if member_result && (site.try_method || site.documented_null) { + return true; + } + // Collection elements, including the results of `get_at`, `lookup` and + // `current`, follow the collection holding them. + matches!(site.position, CollectionElement | Return | Property) + && site.container.is_some_and(element_admits_none) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn method(raw_name: &str) -> MethodMeta { + MethodMeta { + name: raw_name.into(), + raw_name: raw_name.into(), + ..Default::default() + } + } + + #[test] + fn try_methods_require_an_uppercase_continuation() { + assert!(is_try_method(&method("TryParse"))); + assert!(is_try_method(&method("TryGetItemAsync"))); + assert!(!is_try_method(&method("Try"))); + assert!(!is_try_method(&method("Trying"))); + assert!(!is_try_method(&method("GetTryCount"))); + assert!(!is_try_method(&method("get_TryCount"))); + } + + #[test] + fn nested_sites_keep_member_facts() { + let site = OutputSite::for_method(&method("TryGetItemAsync"), OutputPosition::Return); + assert_eq!( + site.nested(OutputPosition::AsyncResult), + OutputSite { + position: OutputPosition::AsyncResult, + try_method: true, + documented_null: false, + container: None, + } + ); + assert_eq!( + site.element_in(ElementContainer::Array), + OutputSite { + position: OutputPosition::CollectionElement, + try_method: true, + documented_null: false, + container: Some(ElementContainer::Array), + } + ); + } + + const POSITIONS: [OutputPosition; 8] = [ + OutputPosition::Return, + OutputPosition::OutParam, + OutputPosition::Property, + OutputPosition::AsyncResult, + OutputPosition::AsyncProgress, + OutputPosition::CollectionElement, + OutputPosition::CallbackParam, + OutputPosition::Activation, + ]; + + fn widget() -> TypeMeta { + TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "Widget".into(), + default_interface: None, + } + } + + fn handler() -> TypeMeta { + TypeMeta::Delegate { + namespace: "Contoso".into(), + name: "Handler".into(), + iid: "11111111-1111-1111-1111-111111111111".into(), + } + } + + fn nullable_u32() -> TypeMeta { + TypeMeta::Parameterized { + namespace: "Windows.Foundation".into(), + name: "IReference`1".into(), + piid: "61c17706-2d65-11e0-9ae8-d48564015472".into(), + args: vec![TypeMeta::U32], + } + } + + fn admits(typ: &TypeMeta, site: OutputSite, surface: AnnotationSurface) -> bool { + output_admits_none(typ, site, surface, &PythonProjectionContext::default()) + } + + #[test] + fn runtime_annotations_stay_pessimistic() { + for position in POSITIONS { + let site = OutputSite::of(position); + let expected = position != OutputPosition::Activation; + for typ in [widget(), handler(), TypeMeta::Object] { + assert_eq!( + admits(&typ, site, AnnotationSurface::Runtime), + expected, + "{typ:?} at {position:?}" + ); + } + assert!(admits(&nullable_u32(), site, AnnotationSurface::Runtime)); + assert!(!admits(&TypeMeta::String, site, AnnotationSurface::Runtime)); + } + } + + #[test] + fn stubs_are_non_null_by_default() { + for position in POSITIONS { + let site = OutputSite::of(position); + assert!( + !admits(&widget(), site, AnnotationSurface::Stub), + "{position:?}" + ); + assert!(!admits(&TypeMeta::I32, site, AnnotationSurface::Stub)); + } + } + + #[test] + fn stub_exceptions_keep_none() { + for position in POSITIONS { + let site = OutputSite::of(position); + assert!(admits(&nullable_u32(), site, AnnotationSurface::Stub)); + assert!(admits(&TypeMeta::Object, site, AnnotationSurface::Stub)); + assert_eq!( + admits(&handler(), site, AnnotationSurface::Stub), + position != OutputPosition::CallbackParam, + "{position:?}" + ); + } + } + + const MEMBER_RESULTS: [OutputPosition; 5] = [ + OutputPosition::Return, + OutputPosition::OutParam, + OutputPosition::Property, + OutputPosition::AsyncResult, + OutputPosition::Activation, + ]; + + #[test] + fn try_members_keep_none_on_their_results_only() { + let try_get = method("TryGetItemAsync"); + for position in POSITIONS { + assert_eq!( + admits( + &widget(), + OutputSite::for_method(&try_get, position), + AnnotationSurface::Stub + ), + MEMBER_RESULTS.contains(&position), + "{position:?}" + ); + } + assert!(!admits( + &TypeMeta::Bool, + OutputSite::for_method(&try_get, OutputPosition::Return), + AnnotationSurface::Stub + )); + } + + #[test] + fn documented_null_members_keep_none_on_their_results_only() { + let get_default = MethodMeta { + documented_null_result: true, + ..method("GetDefault") + }; + for position in POSITIONS { + assert_eq!( + admits( + &widget(), + OutputSite::for_method(&get_default, position), + AnnotationSurface::Stub + ), + MEMBER_RESULTS.contains(&position), + "{position:?}" + ); + } + let site = OutputSite::for_method(&get_default, OutputPosition::Return); + assert!(!admits( + &widget(), + site.element_in(ElementContainer::View), + AnnotationSurface::Stub + )); + } + + #[test] + fn collection_elements_follow_the_mutability_of_their_collection() { + let stub = AnnotationSurface::Stub; + assert!(admits( + &widget(), + OutputSite::element_of(ElementContainer::Mutable), + stub + )); + for container in [ElementContainer::View, ElementContainer::Array] { + let site = OutputSite::element_of(container); + assert!(!admits(&widget(), site, stub), "{container:?}"); + assert!(admits(&TypeMeta::Object, site, stub)); + assert!(admits(&nullable_u32(), site, stub)); + assert!(!admits(&TypeMeta::String, site, stub)); + } + for (access, expected) in [ + (ElementAccess::Mutable, true), + (ElementAccess::ReadOnly, false), + ] { + let get_at = MethodMeta { + element_access: Some(access), + ..method("GetAt") + }; + for position in [OutputPosition::Return, OutputPosition::Property] { + assert_eq!( + admits(&widget(), OutputSite::for_method(&get_at, position), stub), + expected, + "{access:?} at {position:?}" + ); + } + assert!(!admits( + &widget(), + OutputSite::for_method(&get_at, OutputPosition::AsyncProgress), + stub + )); + } + assert_eq!( + ElementContainer::of(CollectionKind::MutableSequence), + ElementContainer::Mutable + ); + assert_eq!( + ElementContainer::of(CollectionKind::KeyValuePair), + ElementContainer::View + ); + } +} diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs index 316b4319..37ba7e3f 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs @@ -9,10 +9,12 @@ use crate::types::{FieldMeta, TypeMeta}; use super::naming::{PythonProjectionContext, PythonSymbol, STRUCT_SYMBOLS, to_snake_case}; use super::native_types::{FoundationType, foundation_type}; +use super::nullability::{AnnotationSurface, ElementContainer}; use super::structs::{py_struct_field_read_type, py_struct_field_type}; use super::type_helpers::{ - method_pydoc_with_indent, py_delegate_callable_type, py_factory_return_type, - py_method_return_type, py_output_type, py_param_list, py_param_type_safe, + method_pydoc_with_indent, py_collection_item_type, py_delegate_callable_type, + py_factory_return_type, py_method_return_type, py_param_list, py_param_type_safe, + py_property_type, }; use crate::codegen::winrt::shared::imports::ireference_inner_type; @@ -274,7 +276,7 @@ pub(super) fn emit_method_stub_named( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_output_type(typ, context)) + .map(|typ| py_property_type(method, typ, AnnotationSurface::Stub, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(&format!("{indent}@builtins.property\n")); emit_documented_stub( @@ -317,7 +319,7 @@ pub(super) fn emit_method_stub_named( } } else { let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Stub, context); let method_name = name_override .map(str::to_string) .unwrap_or_else(|| to_snake_case(&method.name)); @@ -326,15 +328,21 @@ pub(super) fn emit_method_stub_named( } else { format!("self, {}", py_params) }; - // WinRT vectors may reject null on mutation while returning null - // interface elements. That asymmetric native contract cannot satisfy - // MutableSequence[T | None]'s append signature exactly. Empty - // structural protocols can make mypy consider the override compatible. + // `append` takes the projected input annotation, while the + // MutableSequence base reads elements back as `T | None`: WinRT + // vectors may reject null on mutation, yet anyone can store null in + // them. Empty structural protocols can make mypy consider the + // override compatible. let override_ignore = if overrides_mutable_sequence && method_name == "append" && in_params.first().is_some_and(|param| { py_param_type_safe(¶m.typ, context) - != super::type_helpers::py_return_type_safe(Some(¶m.typ), context) + != py_collection_item_type( + ¶m.typ, + ElementContainer::Mutable, + AnnotationSurface::Stub, + context, + ) }) { " # type: ignore[override, unused-ignore]" } else { @@ -372,9 +380,9 @@ pub(super) fn emit_static_method_stub_named( let py_params = py_param_list(&in_params, context); let py_return = if is_factory { - py_factory_return_type(class_name, method, context) + py_factory_return_type(class_name, method, AnnotationSurface::Stub, context) } else { - py_method_return_type(method, context) + py_method_return_type(method, AnnotationSurface::Stub, context) }; let mut out = String::new(); diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs index 0568bbf9..3dcd6d24 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs @@ -25,10 +25,11 @@ use crate::codegen::winrt::shared::structs::{ }; use super::collections::{ - CollectionKind, abc_name, class_interface, interface_kind, observable_vector_identity, + CollectionKind, class_interface, interface_kind, observable_vector_identity, }; use super::naming::{PythonProjectionContext, PythonSupportSymbol, is_py_reserved, to_snake_case}; use super::native_types::foundation_type; +use super::nullability::{AnnotationSurface, ElementContainer}; use super::shared::reorder_getters_before_setters; use super::signature::py_dynwinrt_type; use super::stub_helpers::{ @@ -451,23 +452,14 @@ pub fn generate_interface_stub(context: &PythonProjectionContext, iface: &Interf } } - let collection_base = - collection_kind - .and_then(abc_name) - .and_then(|abc| match iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", - abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", - abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, - }); + let collection_base = collection_kind.and_then(|kind| { + super::type_helpers::py_collection_base_type( + kind, + &iface.generic_args, + AnnotationSurface::Stub, + context, + ) + }); let identity_name = format!("_{}Identity", iface.name); out.push_str(&format!("\nclass {identity_name}(Protocol):\n")); out.push_str(&format!(" def {marker}(self) -> None: ...\n")); @@ -864,20 +856,14 @@ pub fn generate_class_stub( } let collection_base = collection_iface - .zip(collection_kind.and_then(abc_name)) - .and_then(|(iface, abc)| match iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", - abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", - abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, + .zip(collection_kind) + .and_then(|(iface, kind)| { + super::type_helpers::py_collection_base_type( + kind, + &iface.generic_args, + AnnotationSurface::Stub, + context, + ) }); let mut instance_stub_body = emit_class_instance_stubs(class, context, collection_iface, false, has_closable); @@ -1059,22 +1045,14 @@ pub fn generate_class_stub( continue; } out.push('\n'); - let required_base = interface_kind(req_iface) - .and_then(abc_name) - .and_then(|abc| match req_iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", - abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", - abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, - }); + let required_base = interface_kind(req_iface).and_then(|kind| { + super::type_helpers::py_collection_base_type( + kind, + &req_iface.generic_args, + AnnotationSurface::Stub, + context, + ) + }); if let Some(base) = required_base { out.push_str(&format!("\nclass {symbol}({base}):\n")); } else { @@ -1295,10 +1273,19 @@ fn collection_protocol_stubs( return String::new(); }; let indent = " ".repeat(indent_spaces); + // Item positions inherit the element rule of the collection that owns them. + let container = ElementContainer::of(kind); let item_type = iface .generic_args .first() - .map(|typ| super::type_helpers::py_return_type_safe(Some(typ), context)) + .map(|typ| { + super::type_helpers::py_collection_item_type( + typ, + container, + AnnotationSurface::Stub, + context, + ) + }) .unwrap_or_else(|| "object".to_string()); let item_input = iface .generic_args @@ -1341,7 +1328,14 @@ fn collection_protocol_stubs( let value_type = iface .generic_args .get(1) - .map(|typ| super::type_helpers::py_return_type_safe(Some(typ), context)) + .map(|typ| { + super::type_helpers::py_collection_item_type( + typ, + container, + AnnotationSurface::Stub, + context, + ) + }) .unwrap_or_else(|| "object".to_string()); let mut result = format!( "\n{indent}def __len__(self) -> int: ...\n\ diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs index 34d75531..46c9ba5c 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs @@ -10,11 +10,15 @@ use crate::codegen::winrt::shared::imports::{ use crate::meta::MethodMeta; use crate::types::TypeMeta; -use super::collections::{CollectionKind, is_mapping_input, type_kind}; +use super::collections::{CollectionKind, abc_name, is_mapping_input, type_kind}; use super::docs::format_pydoc; use super::naming::to_snake_case; use super::naming::{PythonProjectionContext, PythonSupportSymbol, PythonSymbol}; use super::native_types::{FoundationType, foundation_type}; +use super::nullability::{ + AnnotationSurface, ElementContainer, OutputPosition, OutputSite, may_project_none, + output_admits_none, +}; /// Build the Python docstring for a method body. Uses snake_case param display /// names (matching the generated signature). Returns an empty string when no @@ -58,25 +62,17 @@ pub(super) fn method_pydoc_with_indent( // ====================================================================== pub(crate) fn py_optional_type(typ: String) -> String { - let unquoted = typ - .strip_prefix('\'') - .and_then(|value| value.strip_suffix('\'')) - .unwrap_or(&typ); + let unquoted = unquoted(&typ); if unquoted.split('|').any(|part| part.trim() == "None") { return unquoted.to_string(); } format!("{} | None", unquoted) } -fn is_nullable_reference_type(typ: &TypeMeta) -> bool { - matches!( - typ, - TypeMeta::Object - | TypeMeta::Delegate { .. } - | TypeMeta::RuntimeClass { .. } - | TypeMeta::Interface { .. } - | TypeMeta::Parameterized { .. } - ) +fn unquoted(typ: &str) -> &str { + typ.strip_prefix('\'') + .and_then(|value| value.strip_suffix('\'')) + .unwrap_or(typ) } fn py_param_type(typ: &TypeMeta, context: &PythonProjectionContext) -> String { @@ -152,85 +148,335 @@ pub(super) fn py_collection_input_type( ) -> String { let input = py_param_type_safe(typ, context); // Keep the existing nullable ABC contract; only widen its projected inputs. - if is_nullable_reference_type(typ) { + if may_project_none(typ) { py_optional_type(input) } else { input } } -pub(crate) fn py_return_type_safe( - typ: Option<&TypeMeta>, +// ====================================================================== +// Output annotations +// +// Rendering and nullability are separate layers: the `spell_*` functions +// produce the non-null type expression for a position, and +// `nullability::output_admits_none` alone decides whether `| None` is added. +// ====================================================================== + +/// Spelling rules of the rendering layer. They predate the nullability policy +/// and are preserved byte-for-byte. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Spelling { + /// Method, out and property results: delegates are raw `DynWinRTValue` + /// handles, also inside async results and arrays. + Member, + /// A standalone value, such as the item type of a collection class. + Value, + /// An element of a returned collection or array: nested generics are + /// named by their projected class. + Element, +} + +impl Spelling { + fn at(position: OutputPosition) -> Self { + match position { + OutputPosition::CollectionElement | OutputPosition::CallbackParam => Self::Value, + OutputPosition::Return + | OutputPosition::OutParam + | OutputPosition::Property + | OutputPosition::AsyncResult + | OutputPosition::AsyncProgress + | OutputPosition::Activation => Self::Member, + } + } +} + +/// Renders the annotation of a value the consumer receives at `site`. +/// +/// Nullability never changes how the base type is spelled: a value that may +/// project as `None` renders the same unquoted expression with or without +/// ` | None`. +pub(crate) fn py_output_annotation( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - if let Some(inner) = typ.and_then(ireference_inner_type) { - return py_optional_type(py_return_type_safe(Some(inner), context)); - } - if let Some(async_type) = typ.and_then(|typ| py_async_return_type(typ, context)) { - return async_type; + render_output(typ, Spelling::at(site.position), site, surface, context) +} + +fn render_output( + typ: &TypeMeta, + spelling: Spelling, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + let base = match spelling { + Spelling::Member => spell_member(typ, site, surface, context), + Spelling::Value => spell_value(typ, site, surface, context), + Spelling::Element => py_native_element_type(typ, context), + }; + if !may_project_none(typ) { + return base; } - if let Some(annotation) = typ.and_then(|typ| py_collection_return_type(typ, context)) { - return py_optional_type(annotation); + if output_admits_none(typ, site, surface, context) { + py_optional_type(base) + } else { + unquoted(&base).to_string() } +} +fn spell_member( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + if context.is_delegate_type(typ) { + return "DynWinRTValue".to_string(); + } + let nested = |typ: &TypeMeta, position: OutputPosition| { + render_output( + typ, + Spelling::Member, + site.nested(position), + surface, + context, + ) + }; match typ { - Some(typ @ TypeMeta::Enum { .. }) if !context.is_known_type(typ) => "int".to_string(), - Some(typ @ (TypeMeta::RuntimeClass { .. } | TypeMeta::Interface { .. })) - if !context.is_known_type(typ) => - { - "DynWinRTValue | None".to_string() - } - Some(TypeMeta::Array(inner)) => py_array_return_type(inner, context), - Some(typ) if is_nullable_reference_type(typ) => { - py_optional_type(py_return_type(Some(typ), context)) + TypeMeta::Array(inner) if context.is_delegate_type(inner) => format!( + "list[{}]", + render_output( + inner, + Spelling::Member, + array_element(site), + surface, + context + ) + ), + TypeMeta::AsyncOperation(result) => { + format!( + "WinRTCoroutine[{}]", + nested(result, OutputPosition::AsyncResult) + ) } - _ => py_return_type(typ, context), + TypeMeta::AsyncOperationWithProgress(result, progress) => format!( + "WinRTCoroutineWithProgress[{}, {}]", + nested(result, OutputPosition::AsyncResult), + nested(progress, OutputPosition::AsyncProgress) + ), + TypeMeta::AsyncActionWithProgress(progress) => format!( + "WinRTCoroutineWithProgress[None, {}]", + nested(progress, OutputPosition::AsyncProgress) + ), + _ => spell_value(typ, site, surface, context), } } -fn py_async_return_type_with_result( +fn spell_value( typ: &TypeMeta, - result_override: Option, + site: OutputSite, + surface: AnnotationSurface, context: &PythonProjectionContext, -) -> Option { +) -> String { + if let Some(inner) = ireference_inner_type(typ) { + return render_output(inner, Spelling::Value, site, surface, context); + } + if let Some(collection) = spell_collection(typ, site, surface, context) { + return collection; + } + let nested = |typ: &TypeMeta, spelling: Spelling, position: OutputPosition| { + render_output(typ, spelling, site.nested(position), surface, context) + }; match typ { - TypeMeta::AsyncAction => Some("WinRTCoroutine[None]".to_string()), - TypeMeta::AsyncOperation(result) => Some(format!( + TypeMeta::AsyncAction => "WinRTCoroutine[None]".to_string(), + TypeMeta::AsyncOperation(result) => format!( "WinRTCoroutine[{}]", - result_override.unwrap_or_else(|| py_return_type_safe(Some(result), context)) - )), - TypeMeta::AsyncActionWithProgress(progress) => Some(format!( + nested(result, Spelling::Value, OutputPosition::AsyncResult) + ), + TypeMeta::AsyncActionWithProgress(progress) => format!( "WinRTCoroutineWithProgress[None, {}]", - py_return_type_safe(Some(progress), context) - )), - TypeMeta::AsyncOperationWithProgress(result, progress) => Some(format!( + nested(progress, Spelling::Value, OutputPosition::AsyncProgress) + ), + TypeMeta::AsyncOperationWithProgress(result, progress) => format!( "WinRTCoroutineWithProgress[{}, {}]", - result_override.unwrap_or_else(|| py_return_type_safe(Some(result), context)), - py_return_type_safe(Some(progress), context) - )), - _ => None, + nested(result, Spelling::Value, OutputPosition::AsyncResult), + nested(progress, Spelling::Value, OutputPosition::AsyncProgress) + ), + TypeMeta::Enum { .. } if !context.is_known_type(typ) => "int".to_string(), + TypeMeta::RuntimeClass { .. } | TypeMeta::Interface { .. } + if !context.is_known_type(typ) => + { + "DynWinRTValue".to_string() + } + TypeMeta::Array(inner) if matches!(inner.as_ref(), TypeMeta::U8) => "bytes".to_string(), + TypeMeta::Array(inner) => format!( + "list[{}]", + render_output( + inner, + Spelling::Element, + array_element(site), + surface, + context + ) + ), + TypeMeta::String | TypeMeta::Char16 => "str".to_string(), + TypeMeta::Guid => "UUID".to_string(), + TypeMeta::Bool => "bool".to_string(), + TypeMeta::I8 + | TypeMeta::U8 + | TypeMeta::I16 + | TypeMeta::U16 + | TypeMeta::I32 + | TypeMeta::U32 + | TypeMeta::I64 + | TypeMeta::U64 => "int".to_string(), + TypeMeta::F32 | TypeMeta::F64 => "float".to_string(), + TypeMeta::RuntimeClass { .. } + | TypeMeta::Enum { .. } + | TypeMeta::Interface { .. } + | TypeMeta::Parameterized { .. } => { + format!("'{}'", context.reference_name_for_type(typ)) + } + TypeMeta::Object | TypeMeta::Delegate { .. } => "'DynWinRTValue'".to_string(), + TypeMeta::Struct { name, .. } if name == "HResult" => "int".to_string(), + typ if foundation_type(typ) == Some(FoundationType::DateTime) => "datetime".to_string(), + typ if foundation_type(typ) == Some(FoundationType::TimeSpan) => "timedelta".to_string(), + TypeMeta::Struct { .. } => format!("'{}'", context.reference_name_for_type(typ)), } } -pub(super) fn py_async_return_type( +fn spell_collection( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> Option { + let TypeMeta::Parameterized { args, .. } = typ else { + return None; + }; + let kind = type_kind(typ)?; + let abc = abc_name(kind)?; + let element = site.element_in(ElementContainer::of(kind)); + let elements = args + .iter() + .map(|arg| render_output(arg, Spelling::Element, element, surface, context)) + .collect::>(); + Some(format!("{abc}[{}]", elements.join(", "))) +} + +/// The elements of an array filled by a member that reads collection +/// elements (`get_many`) follow that collection; other arrays are snapshots. +fn array_element(site: OutputSite) -> OutputSite { + site.element_in(site.container.unwrap_or(ElementContainer::Array)) +} + +/// Pessimistic rendering for callers outside the output policy (callback +/// parameters and the `IReference` input arm): every value that may +/// project as `None` admits it, as on the runtime surface. +pub(crate) fn py_return_type_safe( + typ: Option<&TypeMeta>, + context: &PythonProjectionContext, +) -> String { + typ.map(|typ| { + render_output( + typ, + Spelling::Value, + OutputSite::of(OutputPosition::CallbackParam), + AnnotationSurface::Runtime, + context, + ) + }) + .unwrap_or_else(|| "None".to_string()) +} + +/// Annotation of the value read by a property getter. +pub(super) fn py_property_type( + getter: &MethodMeta, + typ: &TypeMeta, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + py_output_annotation( + typ, + OutputSite::for_method(getter, OutputPosition::Property), + surface, + context, + ) +} + +/// Item, key or value type of a projected collection held by `container`. +pub(super) fn py_collection_item_type( typ: &TypeMeta, + container: ElementContainer, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + py_output_annotation(typ, OutputSite::element_of(container), surface, context) +} + +/// `Sequence[T]` / `Mapping[K, V]` base of a projected collection of `kind`. +pub(super) fn py_collection_base_type( + kind: CollectionKind, + args: &[TypeMeta], + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> Option { - py_async_return_type_with_result(typ, None, context) + let abc = abc_name(kind)?; + let item = |typ| py_collection_item_type(typ, ElementContainer::of(kind), surface, context); + match args { + [element] => Some(format!("{abc}[{}]", item(element))), + [key, value] => Some(format!("{abc}[{}, {}]", item(key), item(value))), + _ => None, + } } +/// Result annotation of an activation factory creating `class_name`. pub(super) fn py_factory_return_type( class_name: &str, method: &MethodMeta, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - method - .return_type - .as_ref() - .and_then(|typ| { - py_async_return_type_with_result(typ, Some(format!("'{}'", class_name)), context) - }) - .unwrap_or_else(|| format!("'{}'", class_name)) + let instance = |typ: &TypeMeta| { + let instance = format!("'{class_name}'"); + let site = OutputSite::for_method(method, OutputPosition::Activation); + if output_admits_none(typ, site, surface, context) { + py_optional_type(instance) + } else { + instance + } + }; + let progress = |typ: &TypeMeta| { + render_output( + typ, + Spelling::Value, + OutputSite::for_method(method, OutputPosition::AsyncProgress), + surface, + context, + ) + }; + match method.return_type.as_ref() { + None => format!("'{class_name}'"), + Some(TypeMeta::AsyncAction) => "WinRTCoroutine[None]".to_string(), + Some(TypeMeta::AsyncOperation(result)) => { + format!("WinRTCoroutine[{}]", instance(result)) + } + Some(TypeMeta::AsyncActionWithProgress(progress_type)) => { + format!( + "WinRTCoroutineWithProgress[None, {}]", + progress(progress_type) + ) + } + Some(TypeMeta::AsyncOperationWithProgress(result, progress_type)) => format!( + "WinRTCoroutineWithProgress[{}, {}]", + instance(result), + progress(progress_type) + ), + Some(typ) => instance(typ), + } } pub(super) fn methods_have_async_output<'a>( @@ -249,20 +495,26 @@ pub(super) fn py_method_abi_output_count(method: &MethodMeta) -> usize { } pub(super) fn py_method_outputs(method: &MethodMeta) -> Vec<(usize, &TypeMeta)> { - let mut result_index = 0; + py_method_output_positions(method) + .into_iter() + .enumerate() + .map(|(result_index, (typ, _))| (result_index, typ)) + .collect() +} + +/// Logical outputs in result order: out values first, then the return value. +fn py_method_output_positions(method: &MethodMeta) -> Vec<(&TypeMeta, OutputPosition)> { let mut outputs = Vec::new(); for param in &method.params { match param.direction { crate::meta::ParamDirection::Out => { - outputs.push((result_index, ¶m.typ)); - result_index += 1; + outputs.push((¶m.typ, OutputPosition::OutParam)); } crate::meta::ParamDirection::OutFill => { // The runtime allocates a distinct filled result buffer; the // caller-provided array supplies capacity and is not mutated. - outputs.push((result_index, ¶m.typ)); - result_index += 1; + outputs.push((¶m.typ, OutputPosition::OutParam)); } crate::meta::ParamDirection::In => {} } @@ -273,108 +525,32 @@ pub(super) fn py_method_outputs(method: &MethodMeta) -> Vec<(usize, &TypeMeta)> .as_ref() .filter(|_| !fill_array_uses_retval_count(method)) { - outputs.push((result_index, return_type)); + outputs.push((return_type, OutputPosition::Return)); } outputs } -fn is_delegate_output(typ: &TypeMeta, context: &PythonProjectionContext) -> bool { - context.is_delegate_type(typ) -} - -pub(super) fn py_output_type(typ: &TypeMeta, context: &PythonProjectionContext) -> String { - match typ { - _ if is_delegate_output(typ, context) => "DynWinRTValue | None".to_string(), - TypeMeta::Array(inner) if is_delegate_output(inner, context) => { - "list[DynWinRTValue | None]".to_string() - } - TypeMeta::AsyncOperation(inner) => { - format!("WinRTCoroutine[{}]", py_output_type(inner, context)) - } - TypeMeta::AsyncOperationWithProgress(result, progress) => format!( - "WinRTCoroutineWithProgress[{}, {}]", - py_output_type(result, context), - py_output_type(progress, context) - ), - TypeMeta::AsyncActionWithProgress(progress) => format!( - "WinRTCoroutineWithProgress[None, {}]", - py_output_type(progress, context) - ), - _ => py_return_type_safe(Some(typ), context), - } -} - pub(super) fn py_method_return_type( method: &MethodMeta, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - let outputs = py_method_outputs(method); + let outputs = py_method_output_positions(method) + .into_iter() + .map(|(typ, position)| { + py_output_annotation( + typ, + OutputSite::for_method(method, position), + surface, + context, + ) + }) + .collect::>(); match outputs.as_slice() { [] => "None".to_string(), - [(_, typ)] => py_output_type(typ, context), - _ => format!( - "tuple[{}]", - outputs - .iter() - .map(|(_, typ)| py_output_type(typ, context)) - .collect::>() - .join(", ") - ), - } -} - -fn py_return_type(typ: Option<&TypeMeta>, context: &PythonProjectionContext) -> String { - match typ { - Some(TypeMeta::String) => "str".to_string(), - Some(TypeMeta::Guid) => "UUID".to_string(), - Some(TypeMeta::Bool) => "bool".to_string(), - Some( - TypeMeta::I8 - | TypeMeta::U8 - | TypeMeta::I16 - | TypeMeta::U16 - | TypeMeta::I32 - | TypeMeta::U32 - | TypeMeta::I64 - | TypeMeta::U64, - ) => "int".to_string(), - Some(TypeMeta::Char16) => "str".to_string(), - Some(TypeMeta::F32 | TypeMeta::F64) => "float".to_string(), - Some(typ @ TypeMeta::RuntimeClass { .. }) - | Some(typ @ TypeMeta::Enum { .. }) - | Some(typ @ TypeMeta::Interface { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - Some(typ @ TypeMeta::Parameterized { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - Some(TypeMeta::AsyncOperation(inner)) => { - format!("WinRTCoroutine[{}]", py_return_type(Some(inner), context)) - } - Some(TypeMeta::AsyncOperationWithProgress(result, progress)) => format!( - "WinRTCoroutineWithProgress[{}, {}]", - py_return_type(Some(result), context), - py_return_type(Some(progress), context) - ), - Some(TypeMeta::AsyncAction) => "WinRTCoroutine[None]".to_string(), - Some(TypeMeta::AsyncActionWithProgress(progress)) => format!( - "WinRTCoroutineWithProgress[None, {}]", - py_return_type(Some(progress), context) - ), - Some(TypeMeta::Array(inner)) => py_array_return_type(inner, context), - Some(TypeMeta::Object) | Some(TypeMeta::Delegate { .. }) => "'DynWinRTValue'".to_string(), - Some(TypeMeta::Struct { name, .. }) if name == "HResult" => "int".to_string(), - Some(typ) if foundation_type(typ) == Some(FoundationType::DateTime) => { - "datetime".to_string() - } - Some(typ) if foundation_type(typ) == Some(FoundationType::TimeSpan) => { - "timedelta".to_string() - } - Some(typ @ TypeMeta::Struct { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - None => "None".to_string(), + [output] => output.clone(), + _ => format!("tuple[{}]", outputs.join(", ")), } } @@ -441,20 +617,6 @@ fn py_native_param_element_type(inner: &TypeMeta, context: &PythonProjectionCont } } -fn py_array_return_type(inner: &TypeMeta, context: &PythonProjectionContext) -> String { - if matches!(inner, TypeMeta::U8) { - "bytes".to_string() - } else { - let element = py_native_element_type(inner, context); - let element = if is_nullable_reference_type(inner) { - py_optional_type(element) - } else { - element - }; - format!("list[{element}]") - } -} - fn py_collection_param_type(typ: &TypeMeta, context: &PythonProjectionContext) -> Option { let TypeMeta::Parameterized { args, .. } = typ else { return None; @@ -500,26 +662,6 @@ fn py_collection_param_type(typ: &TypeMeta, context: &PythonProjectionContext) - } } -fn py_collection_return_type(typ: &TypeMeta, context: &PythonProjectionContext) -> Option { - let TypeMeta::Parameterized { args, .. } = typ else { - return None; - }; - let kind = type_kind(typ)?; - let abc = super::collections::abc_name(kind)?; - let types = args - .iter() - .map(|arg| { - let element = py_native_element_type(arg, context); - if is_nullable_reference_type(arg) { - py_optional_type(element) - } else { - element - } - }) - .collect::>(); - Some(format!("{abc}[{}]", types.join(", "))) -} - pub(super) fn py_param_list( in_params: &[&crate::meta::ParamMeta], context: &PythonProjectionContext, @@ -587,6 +729,19 @@ mod tests { use super::*; use crate::meta::{ParamDirection, ParamMeta}; + fn returned( + typ: &TypeMeta, + surface: AnnotationSurface, + context: &PythonProjectionContext, + ) -> String { + py_output_annotation( + typ, + OutputSite::of(OutputPosition::Return), + surface, + context, + ) + } + #[test] fn multi_out_returns_typed_tuple_in_abi_order() { let method = MethodMeta { @@ -613,7 +768,11 @@ mod tests { assert_eq!(outputs[0], (0, &TypeMeta::U32)); assert_eq!(outputs[1], (1, &TypeMeta::Bool)); assert_eq!( - py_method_return_type(&method, &PythonProjectionContext::default()), + py_method_return_type( + &method, + AnnotationSurface::Stub, + &PythonProjectionContext::default() + ), "tuple[int, bool]" ); } @@ -646,7 +805,11 @@ mod tests { (0, &TypeMeta::Array(Box::new(TypeMeta::String))) ); assert_eq!( - py_method_return_type(&method, &PythonProjectionContext::default()), + py_method_return_type( + &method, + AnnotationSurface::Stub, + &PythonProjectionContext::default() + ), "list[str]" ); assert_eq!( @@ -665,10 +828,13 @@ mod tests { #[test] fn object_arrays_return_typed_runtime_values() { - assert_eq!( - py_array_return_type(&TypeMeta::Object, &PythonProjectionContext::default()), - "list[DynWinRTValue | None]" - ); + let array = TypeMeta::Array(Box::new(TypeMeta::Object)); + for surface in [AnnotationSurface::Runtime, AnnotationSurface::Stub] { + assert_eq!( + returned(&array, surface, &PythonProjectionContext::default()), + "list[DynWinRTValue | None]" + ); + } } #[test] @@ -705,7 +871,7 @@ mod tests { assert_eq!(py_param_type_safe(&typ, &context), expected); } assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&TypeMeta::Object, AnnotationSurface::Stub, &context), "DynWinRTValue | None" ); assert_eq!( @@ -739,17 +905,21 @@ mod tests { "DynWinRTValue | _DynWinRTObject_2 | None" ); assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&TypeMeta::Object, AnnotationSurface::Stub, &context), "DynWinRTValue | None" ); assert_eq!( - py_array_return_type(&TypeMeta::Object, &context), + returned( + &TypeMeta::Array(Box::new(TypeMeta::Object)), + AnnotationSurface::Stub, + &context + ), "list[DynWinRTValue | None]" ); } #[test] - fn reference_returns_are_annotated_as_nullable() { + fn runtime_reference_outputs_stay_nullable() { let runtime_class = TypeMeta::RuntimeClass { namespace: "Contoso".into(), name: "Widget".into(), @@ -765,23 +935,215 @@ mod tests { interface.type_identity(), ]) .unwrap(); + let runtime = AnnotationSurface::Runtime; + assert_eq!(returned(&runtime_class, runtime, &context), "Widget | None"); + assert_eq!(returned(&interface, runtime, &context), "IWidget | None"); + assert_eq!( + returned(&TypeMeta::Object, runtime, &context), + "DynWinRTValue | None" + ); + assert_eq!( + returned( + &TypeMeta::Array(Box::new(runtime_class.clone())), + runtime, + &context + ), + "list[Widget | None]" + ); assert_eq!( py_return_type_safe(Some(&runtime_class), &context), "Widget | None" ); + } + + #[test] + fn stub_outputs_are_non_null_except_policy_exceptions() { + let widget = TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "Widget".into(), + default_interface: None, + }; + let interface = TypeMeta::Interface { + namespace: "Contoso".into(), + name: "IWidget".into(), + iid: "11111111-1111-1111-1111-111111111111".into(), + }; + let unknown = TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "NotGenerated".into(), + default_interface: None, + }; + let context = PythonProjectionContext::standalone([ + widget.type_identity(), + interface.type_identity(), + ]) + .unwrap(); + let widgets = TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IVectorView`1".into(), + piid: crate::codegen::winrt::python::collections::IVECTOR_VIEW_PIID.into(), + args: vec![widget.clone()], + }; + let stub = AnnotationSurface::Stub; + let async_of = |typ: &TypeMeta| TypeMeta::AsyncOperation(Box::new(typ.clone())); + let method = |raw_name: &str, params: Vec, return_type: TypeMeta| MethodMeta { + name: raw_name.into(), + raw_name: raw_name.into(), + params, + return_type: Some(return_type), + ..Default::default() + }; + + assert_eq!(returned(&widget, stub, &context), "Widget"); + assert_eq!(returned(&interface, stub, &context), "IWidget"); + assert_eq!(returned(&unknown, stub, &context), "DynWinRTValue"); + assert_eq!( + returned(&TypeMeta::Array(Box::new(widget.clone())), stub, &context), + "list[Widget]" + ); assert_eq!( - py_return_type_safe(Some(&interface), &context), - "IWidget | None" + returned(&async_of(&widget), stub, &context), + "WinRTCoroutine[Widget]" ); assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&async_of(&widgets), stub, &context), + "WinRTCoroutine[Sequence[Widget]]" + ); + assert_eq!( + returned(&TypeMeta::Object, stub, &context), "DynWinRTValue | None" ); + let getter = method("get_Widget", vec![], widget.clone()); + assert_eq!(py_property_type(&getter, &widget, stub, &context), "Widget"); + let documented_getter = MethodMeta { + documented_null_result: true, + ..getter.clone() + }; + assert_eq!( + py_property_type(&documented_getter, &widget, stub, &context), + "Widget | None" + ); + assert_eq!( + py_collection_item_type(&widget, ElementContainer::View, stub, &context), + "Widget" + ); + assert_eq!( + py_collection_item_type(&widget, ElementContainer::Mutable, stub, &context), + "Widget | None" + ); + assert_eq!( + py_collection_base_type( + CollectionKind::Sequence, + std::slice::from_ref(&widget), + stub, + &context + ), + Some("Sequence[Widget]".to_string()) + ); assert_eq!( - py_array_return_type(&runtime_class, &context), + py_collection_base_type( + CollectionKind::MutableMapping, + &[TypeMeta::String, widget.clone()], + stub, + &context + ), + Some("MutableMapping[str, Widget | None]".to_string()) + ); + let vector = |piid: &str| TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IVector`1".into(), + piid: piid.into(), + args: vec![widget.clone()], + }; + for piid in [ + crate::codegen::winrt::python::collections::IVECTOR_PIID, + crate::codegen::winrt::python::collections::IOBSERVABLE_VECTOR_PIID, + ] { + assert_eq!( + returned(&vector(piid), stub, &context), + "MutableSequence[Widget | None]" + ); + } + let get_many = MethodMeta { + params: vec![ParamMeta { + name: "items".into(), + typ: TypeMeta::Array(Box::new(widget.clone())), + direction: ParamDirection::OutFill, + }], + return_type: Some(TypeMeta::U32), + element_access: Some(crate::meta::ElementAccess::Mutable), + ..method("GetMany", vec![], TypeMeta::U32) + }; + assert_eq!( + py_method_return_type(&get_many, stub, &context), "list[Widget | None]" ); + + let get_item = method("GetItemAsync", vec![], async_of(&widget)); + let try_get_item = method("TryGetItemAsync", vec![], async_of(&widget)); + let try_get_items = method("TryGetItemsAsync", vec![], async_of(&widgets)); + let try_parse = method( + "TryParse", + vec![ + ParamMeta { + name: "input".into(), + typ: TypeMeta::String, + direction: ParamDirection::In, + }, + ParamMeta { + name: "result".into(), + typ: widget.clone(), + direction: ParamDirection::Out, + }, + ], + TypeMeta::Bool, + ); + for (method, stub_type, runtime_type) in [ + ( + &get_item, + "WinRTCoroutine[Widget]", + "WinRTCoroutine[Widget | None]", + ), + ( + &try_get_item, + "WinRTCoroutine[Widget | None]", + "WinRTCoroutine[Widget | None]", + ), + ( + &try_get_items, + "WinRTCoroutine[Sequence[Widget] | None]", + "WinRTCoroutine[Sequence[Widget | None] | None]", + ), + ( + &try_parse, + "tuple[Widget | None, bool]", + "tuple[Widget | None, bool]", + ), + ] { + assert_eq!(py_method_return_type(method, stub, &context), stub_type); + assert_eq!( + py_method_return_type(method, AnnotationSurface::Runtime, &context), + runtime_type + ); + } + + let create = method("CreateWidget", vec![], widget.clone()); + let try_create = method("TryCreateWidget", vec![], widget.clone()); + for surface in [AnnotationSurface::Runtime, stub] { + assert_eq!( + py_factory_return_type("Widget", &create, surface, &context), + "'Widget'" + ); + } + assert_eq!( + py_factory_return_type("Widget", &try_create, stub, &context), + "Widget | None" + ); + assert_eq!( + py_factory_return_type("Widget", &try_create, AnnotationSurface::Runtime, &context), + "'Widget'" + ); } #[test] @@ -815,10 +1177,12 @@ mod tests { args: vec![TypeMeta::U32], }; - assert_eq!( - py_return_type_safe(Some(&reference), &PythonProjectionContext::default()), - "int | None" - ); + for surface in [AnnotationSurface::Runtime, AnnotationSurface::Stub] { + assert_eq!( + returned(&reference, surface, &PythonProjectionContext::default()), + "int | None" + ); + } assert_eq!( py_param_type_safe(&reference, &PythonProjectionContext::default()), "int | None | IReference_UInt32" diff --git a/tools/dynwinrt-codegen/src/documented_nulls.rs b/tools/dynwinrt-codegen/src/documented_nulls.rs new file mode 100644 index 00000000..83117a13 --- /dev/null +++ b/tools/dynwinrt-codegen/src/documented_nulls.rs @@ -0,0 +1,324 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Windows SDK members whose documented result can be null. +//! +//! WinRT metadata carries no nullability. `api-docs/windows-null-results.txt` +//! lists the doc comment IDs (`M:`/`P:` api-ids) of the Windows SDK methods +//! and properties whose documentation says the result can be null; +//! `scripts/extract-null-results.py` derives it from MicrosoftDocs/winrt-api. +//! A member is looked up by the type its documentation lists it under, its +//! CLR name, and its parameter types. + +use std::collections::HashSet; +use std::sync::LazyLock; + +use crate::meta::MethodMeta; +use crate::types::TypeMeta; + +const TABLE: &str = include_str!("../api-docs/windows-null-results.txt"); + +struct Table { + members: HashSet, + owners: HashSet, +} + +static DOCUMENTED: LazyLock = LazyLock::new(|| { + let members = entries().map(normalize_api_id).collect::>(); + let owners = members.iter().filter_map(|id| owner_of(id)).collect(); + Table { members, owners } +}); + +/// The api-ids listed in the table, verbatim. +pub(crate) fn entries() -> impl Iterator { + TABLE + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) +} + +/// Whether the documentation of `method`, listed under the type named +/// `owner` (`Namespace.Type`), says its result can be null. +pub(crate) fn documents_null_result(owner: &str, method: &MethodMeta) -> bool { + DOCUMENTED.owners.contains(owner) + && member_id(owner, method).is_some_and(|id| DOCUMENTED.members.contains(&id)) +} + +/// The normalized doc comment ID of `method` listed under `owner`. Only +/// methods and property getters produce results. +pub(crate) fn member_id(owner: &str, method: &MethodMeta) -> Option { + if method.is_property_getter { + let property = method.raw_name.strip_prefix("get_")?; + return Some(format!("P:{owner}.{property}")); + } + if method.is_property_setter || method.is_event_add || method.is_event_remove { + return None; + } + let parameters = method + .params + .iter() + .map(|parameter| doc_type_name(¶meter.typ)) + .collect::>() + .join(","); + Some(format!("M:{owner}.{}({parameters})", method.raw_name)) +} + +/// Normalizes an api-id so that metadata can reproduce it: methods always +/// carry a parameter list, and parameter types lose by-reference markers, +/// modifiers and generic arguments. +pub(crate) fn normalize_api_id(api_id: &str) -> String { + let Some(open) = api_id.find('(') else { + return if api_id.starts_with("M:") { + format!("{api_id}()") + } else { + api_id.to_string() + }; + }; + let parameters = split_parameters(api_id[open + 1..].trim_end_matches(')')) + .into_iter() + .map(normalize_doc_type) + .collect::>() + .join(","); + format!("{}({parameters})", &api_id[..open]) +} + +fn owner_of(id: &str) -> Option { + let member = id.get(2..)?.split('(').next()?; + member.rsplit_once('.').map(|(owner, _)| owner.to_string()) +} + +fn split_parameters(list: &str) -> Vec<&str> { + let mut parameters = Vec::new(); + let (mut depth, mut start) = (0usize, 0usize); + for (index, character) in list.char_indices() { + match character { + '{' => depth += 1, + '}' => depth = depth.saturating_sub(1), + ',' if depth == 0 => { + parameters.push(&list[start..index]); + start = index + 1; + } + _ => {} + } + } + if !list.is_empty() { + parameters.push(&list[start..]); + } + parameters +} + +fn normalize_doc_type(name: &str) -> String { + let name = name.split('!').next().unwrap_or(name).trim_end_matches('@'); + let mut result = String::with_capacity(name.len()); + let mut depth = 0usize; + for character in name.chars() { + match character { + '{' => depth += 1, + '}' => depth = depth.saturating_sub(1), + _ if depth == 0 => result.push(character), + _ => {} + } + } + match result.split_once('`') { + Some((definition, _)) => definition.to_string(), + // Some pages spell this struct with its .NET projection. + None if result == "System.Type" => "Windows.UI.Xaml.Interop.TypeName".to_string(), + None => result, + } +} + +/// The doc comment ID name of a parameter type, after normalization. +fn doc_type_name(typ: &TypeMeta) -> String { + let name = match typ { + TypeMeta::Bool => "System.Boolean", + TypeMeta::I8 => "System.SByte", + TypeMeta::U8 => "System.Byte", + TypeMeta::I16 => "System.Int16", + TypeMeta::U16 => "System.UInt16", + TypeMeta::I32 => "System.Int32", + TypeMeta::U32 => "System.UInt32", + TypeMeta::I64 => "System.Int64", + TypeMeta::U64 => "System.UInt64", + TypeMeta::F32 => "System.Single", + TypeMeta::F64 => "System.Double", + TypeMeta::Char16 => "System.Char", + TypeMeta::String => "System.String", + TypeMeta::Guid => "System.Guid", + TypeMeta::Object => "System.Object", + TypeMeta::AsyncAction => "Windows.Foundation.IAsyncAction", + TypeMeta::AsyncActionWithProgress(_) => "Windows.Foundation.IAsyncActionWithProgress", + TypeMeta::AsyncOperation(_) => "Windows.Foundation.IAsyncOperation", + TypeMeta::AsyncOperationWithProgress(..) => { + "Windows.Foundation.IAsyncOperationWithProgress" + } + TypeMeta::Array(inner) => return format!("{}[]", doc_type_name(inner)), + TypeMeta::Interface { + namespace, name, .. + } + | TypeMeta::RuntimeClass { + namespace, name, .. + } + | TypeMeta::Delegate { + namespace, name, .. + } + | TypeMeta::Struct { + namespace, name, .. + } + | TypeMeta::Enum { + namespace, name, .. + } + | TypeMeta::Parameterized { + namespace, name, .. + } => { + let definition = name.split('`').next().unwrap_or(name); + return format!("{namespace}.{definition}"); + } + }; + name.to_string() +} + +#[cfg(test)] +mod tests { + use std::collections::{BTreeMap, BTreeSet}; + use std::path::Path; + + use super::*; + use crate::meta::{ParamDirection, ParamMeta}; + + const WINDOWS_WINMD: &str = + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; + + #[test] + fn api_ids_normalize_to_metadata_reproducible_keys() { + assert_eq!( + normalize_api_id("M:Windows.Devices.Sensors.Compass.GetDefault"), + "M:Windows.Devices.Sensors.Compass.GetDefault()" + ); + assert_eq!( + normalize_api_id( + "M:N.T.Find(Windows.Foundation.Collections.IMap{System.String,Windows.Foundation.Collections.IVector{System.String}},System.Byte[]@,System.Guid@!System.Runtime.CompilerServices.IsConst)" + ), + "M:N.T.Find(Windows.Foundation.Collections.IMap,System.Byte[],System.Guid)" + ); + assert_eq!(normalize_api_id("P:N.T.Value"), "P:N.T.Value"); + assert_eq!( + normalize_api_id("M:N.T.Get(System.Type)"), + normalize_api_id("M:N.T.Get(Windows.UI.Xaml.Interop.TypeName)") + ); + assert_eq!(owner_of("M:N.T.Find()").as_deref(), Some("N.T")); + } + + #[test] + fn member_ids_use_clr_names_and_parameter_types() { + let method = MethodMeta { + name: "GetDefaultWithAccelerometerReadingType".into(), + raw_name: "GetDefault".into(), + params: vec![ + ParamMeta { + name: "readingType".into(), + typ: TypeMeta::Enum { + namespace: "Windows.Devices.Sensors".into(), + name: "AccelerometerReadingType".into(), + underlying: Box::new(TypeMeta::I32), + members: vec![], + is_flags: false, + doc: None, + deprecated: None, + }, + direction: ParamDirection::In, + }, + ParamMeta { + name: "values".into(), + typ: TypeMeta::Array(Box::new(TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IIterable`1".into(), + piid: String::new(), + args: vec![TypeMeta::String], + })), + direction: ParamDirection::Out, + }, + ], + ..Default::default() + }; + assert_eq!( + member_id("Windows.Devices.Sensors.Accelerometer", &method).as_deref(), + Some( + "M:Windows.Devices.Sensors.Accelerometer.GetDefault(Windows.Devices.Sensors.AccelerometerReadingType,Windows.Foundation.Collections.IIterable[])" + ) + ); + let getter = MethodMeta { + name: "get_Parent".into(), + raw_name: "get_Parent".into(), + is_property_getter: true, + ..Default::default() + }; + assert_eq!( + member_id("Windows.UI.Xaml.FrameworkElement", &getter).as_deref(), + Some("P:Windows.UI.Xaml.FrameworkElement.Parent") + ); + let setter = MethodMeta { + name: "put_Parent".into(), + raw_name: "put_Parent".into(), + is_property_setter: true, + ..Default::default() + }; + assert_eq!(member_id("N.T", &setter), None); + } + + /// Every entry names a member of the Windows SDK metadata whose parsed + /// method carries the documented-null fact. This guards the table against + /// typos and drift, and the key derivation against metadata changes. + #[test] + fn every_entry_resolves_to_a_flagged_windows_sdk_member() { + // Documented, but absent from the 10.0.26100 SDK metadata: newer APIs, + // and AllJoyn, which the SDK dropped. + const ABSENT_FROM_TEST_SDK: [&str; 3] = [ + "M:Windows.Devices.AllJoyn.AllJoynServiceInfo.FromIdAsync(System.String)", + "M:Windows.Gaming.UI.GameMonitor.GetDefault()", + "M:Windows.System.User.GetUserAgeRangeAsync()", + ]; + if !Path::new(WINDOWS_WINMD).is_file() { + eprintln!("Skipping: Windows.winmd not found"); + return; + } + let index = crate::meta::load_index(WINDOWS_WINMD).expect("Windows.winmd index"); + let mut by_owner = BTreeMap::>::new(); + for entry in entries() { + let id = normalize_api_id(entry); + let owner = owner_of(&id).expect("owner"); + by_owner.entry(owner).or_default().insert(id); + } + let mut unresolved = Vec::new(); + let mut unflagged = Vec::new(); + for (owner, ids) in &by_owner { + let (namespace, name) = owner.rsplit_once('.').expect("qualified owner"); + let methods = crate::meta::documented_owner_methods(&index, namespace, name); + for id in ids { + let matches = methods + .iter() + .filter(|method| member_id(owner, method).as_deref() == Some(id)) + .collect::>(); + if ABSENT_FROM_TEST_SDK.contains(&id.as_str()) { + let member = id[2..] + .split('(') + .next() + .unwrap() + .rsplit('.') + .next() + .unwrap(); + assert!( + !methods.iter().any(|method| method.raw_name == member), + "{id} is present in the test SDK; drop it from ABSENT_FROM_TEST_SDK" + ); + } else if matches.is_empty() { + unresolved.push(id.clone()); + } else if !matches.iter().any(|method| method.documented_null_result) { + unflagged.push(id.clone()); + } + } + } + assert!(unresolved.is_empty(), "unresolved entries: {unresolved:#?}"); + assert!(unflagged.is_empty(), "entries not applied: {unflagged:#?}"); + assert!(by_owner.values().map(BTreeSet::len).sum::() > 900); + } +} diff --git a/tools/dynwinrt-codegen/src/lib.rs b/tools/dynwinrt-codegen/src/lib.rs index ef0c3b86..09db795e 100644 --- a/tools/dynwinrt-codegen/src/lib.rs +++ b/tools/dynwinrt-codegen/src/lib.rs @@ -5,6 +5,7 @@ pub mod codegen; mod com_activation_registry; pub mod com_metadata; mod contract_registry; +mod documented_nulls; pub mod meta; pub mod types; mod win32_contracts; diff --git a/tools/dynwinrt-codegen/src/meta.rs b/tools/dynwinrt-codegen/src/meta.rs index 93e51ee8..729b504c 100644 --- a/tools/dynwinrt-codegen/src/meta.rs +++ b/tools/dynwinrt-codegen/src/meta.rs @@ -57,6 +57,46 @@ pub struct MethodMeta { pub param_docs: std::collections::HashMap, /// XML `` text. pub returns_doc: Option, + /// The Windows SDK documentation says the result can be null: a method's + /// return value (for asynchronous methods, the completed result) or a + /// property's value. See `documented_nulls`. + pub documented_null_result: bool, + /// Set on the members that read elements of the + /// `Windows.Foundation.Collections` interface declaring them. + pub element_access: Option, +} + +/// How a member reads the elements of the `Windows.Foundation.Collections` +/// interface declaring it: `GetAt`, `GetMany`, `Lookup`, `Current`, `Key` and +/// `Value`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ElementAccess { + /// `IIterator`, `IVectorView`, `IMapView` and `IKeyValuePair`. + ReadOnly, + /// `IVector` and `IMap`, in which anyone can store null. + Mutable, +} + +fn collection_element_access( + namespace: &str, + definition: &str, + member: &str, +) -> Option { + if namespace != WINDOWS_FOUNDATION_COLLECTIONS_NAMESPACE { + return None; + } + let access = match definition { + "IIterator`1" | "IVectorView`1" | "IMapView`2" | "IKeyValuePair`2" => { + ElementAccess::ReadOnly + } + "IVector`1" | "IMap`2" => ElementAccess::Mutable, + _ => return None, + }; + matches!( + member, + "GetAt" | "GetMany" | "Lookup" | "get_Current" | "get_Key" | "get_Value" + ) + .then_some(access) } /// A WinRT interface with its methods. @@ -1224,6 +1264,8 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> let mut ancestor_key: Option<(String, String)> = def .extends() .map(|e| (e.namespace().to_string(), e.name().to_string())); + // The documentation lists inherited members under the class declaring them. + let mut documentation_owners = vec![full_name.clone()]; while let Some((ext_ns, ext_name)) = ancestor_key.take() { if ext_ns == "System" && ext_name == "Object" { break; @@ -1232,6 +1274,7 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> Some(d) => d, None => break, }; + documentation_owners.push(format!("{ext_ns}.{ext_name}")); for iface_impl in parent_def.interface_impls() { let iface_ty = iface_impl.interface(&[]); if iface_impl.has_attribute("OverridableAttribute") { @@ -1378,6 +1421,19 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> } } + for interface in default_interface + .iter_mut() + .chain(required_interfaces.iter_mut()) + .chain(factory_interfaces.iter_mut()) + .chain(static_interfaces.iter_mut()) + { + for method in &mut interface.methods { + method.documented_null_result |= documentation_owners + .iter() + .any(|owner| crate::documented_nulls::documents_null_result(owner, method)); + } + } + Some(ClassMeta { name: name.to_string(), namespace: namespace.to_string(), @@ -1428,6 +1484,36 @@ fn parse_interface(index: &reader::Index, namespace: &str, name: &str) -> Option parse_interface_methods(index, &def, name, namespace, &iid, &[]) } +/// The parsed methods that the documentation lists under `namespace.name`: +/// the members of a class's interfaces, or an interface's own members. +#[cfg(test)] +pub(crate) fn documented_owner_methods( + index: &reader::Index, + namespace: &str, + name: &str, +) -> Vec { + let Some(def) = index.get(namespace, name).next() else { + return Vec::new(); + }; + if def.extends().is_none() { + return parse_interface(index, namespace, name) + .map(|interface| interface.methods) + .unwrap_or_default(); + } + parse_class_from_index(index, namespace, name) + .map(|class| { + class + .default_interface + .into_iter() + .chain(class.required_interfaces) + .chain(class.factory_interfaces) + .chain(class.static_interfaces) + .flat_map(|interface| interface.methods) + .collect() + }) + .unwrap_or_default() +} + fn parse_interface_type( index: &reader::Index, interface_type: &windows_metadata::Type, @@ -1582,6 +1668,9 @@ fn parse_interface_methods( ) -> Option { let winmd_generics: Vec = generic_args.iter().map(type_meta_to_winmd_type).collect(); + // The documentation lists interface members under the interface + // definition, e.g. `Windows.Foundation.Collections.IVector`1`. + let documentation_owner = format!("{namespace}.{}", def.name()); let mut methods = Vec::new(); let mut implementation_metadata = InterfaceImplementationMetadata { @@ -1741,7 +1830,8 @@ fn parse_interface_methods( format!("({})", clr_sig_types.join(",")) }; - methods.push(MethodMeta { + let element_access = collection_element_access(namespace, def.name(), &raw_name); + let mut method_meta = MethodMeta { name: method_name.clone(), vtable_index, params, @@ -1756,7 +1846,12 @@ fn parse_interface_methods( deprecated: None, param_docs: std::collections::HashMap::new(), returns_doc: None, - }); + documented_null_result: false, + element_access, + }; + method_meta.documented_null_result = + crate::documented_nulls::documents_null_result(&documentation_owner, &method_meta); + methods.push(method_meta); } let (generic_piid, generic_args_vec) = if !generic_args.is_empty() { diff --git a/tools/dynwinrt-codegen/tests/implementation_naming_test.rs b/tools/dynwinrt-codegen/tests/implementation_naming_test.rs index 16bd74e5..02a888a9 100644 --- a/tools/dynwinrt-codegen/tests/implementation_naming_test.rs +++ b/tools/dynwinrt-codegen/tests/implementation_naming_test.rs @@ -1116,10 +1116,10 @@ fn python_full_identity_collision_imports_named_peer_not_generic_self() { from pyviews.{peer_module} import {peer_name} as Peer\n\ from pyviews.{foreign_module} import {foreign_name} as Foreign\n\ def check(box: Box, peer: Peer, foreign: Foreign) -> None:\n\ - \x20 assert_type(box.echo_self(box), Box | None)\n\ - \x20 assert_type(box.echo_peer(peer), Peer | None)\n\ - \x20 assert_type(box.echo_foreign(foreign), Foreign | None)\n\ - \x20 assert_type(peer.echo_self(peer), Peer | None)\n" + \x20 assert_type(box.echo_self(box), Box)\n\ + \x20 assert_type(box.echo_peer(peer), Peer)\n\ + \x20 assert_type(box.echo_foreign(foreign), Foreign)\n\ + \x20 assert_type(peer.echo_self(peer), Peer)\n" ), ); typecheck_py_package(&fixture.0); diff --git a/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs b/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs index 9b73889b..65ee2ac7 100644 --- a/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs +++ b/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs @@ -72,7 +72,7 @@ fn system_returned_class_keeps_only_internal_native_wrapping() { assert!(!py.contains("self._set_native(type(self).create(")); assert!(!py.contains("_IActivationFactory =")); assert!(pyi.contains("def __init__(self, _not_constructible: NoReturn) -> None: ...")); - assert!(pyi.contains("def get_current() -> SystemResult | None: ...")); + assert!(pyi.contains("def get_current() -> SystemResult: ...")); assert!(!pyi.contains("def from_value("), "{pyi}"); assert!(!pyi.contains("def __init__(self, obj: DynWinRTValue)")); assert!(!pyi.contains("def __init__(self)")); diff --git a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs index 622a464b..34c7b778 100644 --- a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs @@ -813,6 +813,160 @@ print("collection-subscript-native-ok", flush=True) } } +#[test] +fn natural_sdk_consumers_guard_only_nullable_results() { + let winmd = Path::new( + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd", + ); + if !winmd.is_file() || !has_mypy() { + eprintln!("Skipping natural SDK consumers: Windows.winmd or mypy unavailable."); + return; + } + let fixture = Fixture::new(); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args(["generate", "--winmd"]) + .arg(winmd) + .args([ + "--class-name", + "Windows.Foundation.Uri,Windows.Foundation.Collections.PropertySet,\ + Windows.Data.Json.JsonObject,Windows.Globalization.Calendar,\ + Windows.Security.Cryptography.CryptographicBuffer,\ + Windows.Security.Cryptography.Core.HashAlgorithmProvider,\ + Windows.Storage.StorageFolder,Windows.Storage.FileIO,\ + Windows.Storage.Streams.DataReader,Windows.Storage.Streams.DataWriter,\ + Windows.Storage.Streams.InMemoryRandomAccessStream,\ + Windows.Devices.Sensors.Accelerometer", + "--lang", + "py", + "--output", + ]) + .arg(fixture.0.join("sdk")) + .output() + .unwrap(); + assert!(output.status.success(), "{}", diagnostics(&output)); + let imports = r#"from collections.abc import Sequence +from typing import assert_type +from dynwinrt import DynWinRTValue, WinRTCoroutine +from sdk.windows.data.json import IJsonValue, JsonObject +from sdk.windows.devices.sensors import Accelerometer +from sdk.windows.foundation import Uri +from sdk.windows.foundation.collections import PropertySet +from sdk.windows.globalization import Calendar +from sdk.windows.security.cryptography import CryptographicBuffer +from sdk.windows.security.cryptography.core import HashAlgorithmProvider +from sdk.windows.storage import CreationCollisionOption, FileIO, IStorageItem, StorageFile, StorageFolder +from sdk.windows.storage.streams import DataReader, DataWriter, InMemoryRandomAccessStream +"#; + typecheck( + &fixture, + &["sdk"], + &format!( + r#"{imports} +def uri_demo() -> str: + uri = Uri("https://example.com/a/b?x=1&y=two") + query = {{entry.name: entry.value for entry in uri.query_parsed}} + return uri.combine_uri("c/d").absolute_uri + str(query) + +def json_demo() -> list[str]: + parsed = JsonObject.parse('{{"tags": ["a", "b"]}}') + assert_type(JsonObject.try_parse("{{}}"), tuple[JsonObject | None, bool]) + tags = parsed.get_named_array("tags") + assert_type(tags[0], IJsonValue | None) + return [value.get_string() for value in tags if value is not None] + +def sensor_demo() -> float | None: + accelerometer = Accelerometer.get_default() + if accelerometer is None: + return None + return accelerometer.get_current_reading().acceleration_x + +def calendar_demo(calendar: Calendar) -> str: + languages: Sequence[str] = calendar.languages + return languages[0] + +def crypto_demo(data: bytes) -> str: + buffer = CryptographicBuffer.create_from_byte_array(data) + digest = HashAlgorithmProvider.open_algorithm("SHA256").hash_data(buffer) + return CryptographicBuffer.encode_to_hex_string(digest) + +def object_values(properties: PropertySet) -> DynWinRTValue | None: + assert_type(properties["count"], DynWinRTValue | None) + return properties.lookup("count") + +async def streams_demo() -> str: + stream = InMemoryRandomAccessStream() + writer = DataWriter(stream.get_output_stream_at(0)) + writer.write_string("streamed text") + written = await writer.store_async() + reader = DataReader(stream.get_input_stream_at(0)) + return reader.read_string(await reader.load_async(written)) + +async def storage_demo(path: str) -> list[str]: + folder = await StorageFolder.get_folder_from_path_async(path) + file = await folder.create_file_async("notes.txt", CreationCollisionOption.ReplaceExisting) + await FileIO.write_text_async(file, "first line") + assert_type(folder.create_file_async("a.txt"), WinRTCoroutine[StorageFile]) + assert_type(folder.try_get_item_async("notes.txt"), WinRTCoroutine[IStorageItem | None]) + assert_type(folder.get_parent_async(), WinRTCoroutine[StorageFolder | None]) + return [item.name for item in await folder.get_files_async()] +"# + ), + &[], + ); +} + +#[test] +fn mutable_collection_mutators_accept_none() { + let winmd = Path::new( + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd", + ); + if !winmd.is_file() || !has_mypy() { + eprintln!("Skipping mutable collection mutators: Windows.winmd or mypy unavailable."); + return; + } + let fixture = Fixture::new(); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args(["generate", "--winmd"]) + .arg(winmd) + .args([ + "--class-name", + "Windows.Storage.StorageLibrary,Windows.Data.Json.JsonObject", + "--lang", + "py", + "--output", + ]) + .arg(fixture.0.join("sdk")) + .output() + .unwrap(); + assert!(output.status.success(), "{}", diagnostics(&output)); + // Inherited MutableSequence and MutableMapping mutators take the element + // type of the collection base, which keeps `| None` for mutable + // collections, like the generated item setters. + typecheck( + &fixture, + &["sdk"], + r#"from typing import assert_type +from sdk.windows.data.json import IJsonValue, JsonObject +from sdk.windows.foundation.collections import IObservableVector_StorageFolder +from sdk.windows.storage import StorageFolder + +def vector(folders: IObservableVector_StorageFolder) -> None: + folders.append(None) + folders.extend([None]) + folders.insert(0, None) + folders[0] = None + assert_type(folders[0], StorageFolder | None) + +def mapping(values: JsonObject) -> None: + values.update({"k": None}) + values.setdefault("k", None) + values["k"] = None + assert_type(values["k"], IJsonValue | None) +"#, + &[], + ); +} + #[test] fn native_object_inputs_keep_projection_factories_and_context_lifetimes() { if !has_implementation_runtime() { diff --git a/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs b/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs index bdf317d0..2bd3505d 100644 --- a/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs @@ -135,7 +135,7 @@ fn stubs_model_runtime_class_and_interface_bases_without_runtime_inheritance() { "{class_stub}" ); assert!( - class_stub.contains("def use_base(self, value: 'BaseLike') -> Base | None:"), + class_stub.contains("def use_base(self, value: 'BaseLike') -> Base:"), "{class_stub}" ); assert!( diff --git a/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs new file mode 100644 index 00000000..a0dba5e7 --- /dev/null +++ b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs @@ -0,0 +1,275 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The stub nullability policy on real Windows SDK metadata. Values received +//! from the projection are non-null in `.pyi` stubs by default, while +//! `IReference`, `Try*` results, members documented to return null, +//! `Object` and delegate values keep `| None`. Collection elements follow the +//! mutability of the collection holding them. Inputs, implementation +//! protocols and the runtime `.py` annotations are unchanged. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const WINDOWS_WINMD: &str = + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; + +struct Generated(PathBuf); + +impl Drop for Generated { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +impl Generated { + fn new(label: &str, classes: &str) -> Option { + if !Path::new(WINDOWS_WINMD).is_file() { + eprintln!("Skipping: Windows.winmd not found"); + return None; + } + let root = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("target") + .join(format!("pn{label}{}", std::process::id())); + let _ = fs::remove_dir_all(&root); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args([ + "generate", + "--winmd", + WINDOWS_WINMD, + "--class-name", + classes, + ]) + .args(["--lang", "py", "--output"]) + .arg(&root) + .output() + .unwrap(); + assert!( + output.status.success(), + "{}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + Some(Self(root)) + } + + fn module(&self, name: &str) -> String { + fs::read_to_string(self.0.join(name)).unwrap_or_else(|error| panic!("{name}: {error}")) + } +} + +fn assert_contains(text: &str, expected: &str) { + assert!(text.contains(expected), "missing `{expected}`"); +} + +#[test] +fn stub_outputs_follow_the_nullability_policy() { + let Some(generated) = Generated::new( + "out", + "Windows.Storage.StorageFolder,Windows.Web.Http.Headers.HttpContentHeaderCollection,\ + Windows.Foundation.Collections.PropertySet,Windows.Data.Json.JsonObject,\ + Windows.Devices.Sensors.Accelerometer,Windows.Devices.Sensors.Compass,\ + Windows.System.DispatcherQueue,Windows.Data.Xml.Dom.XmlDocument", + ) else { + return; + }; + let folder = generated.module("windows__storage__storage_folder.pyi"); + let folder_py = generated.module("windows__storage__storage_folder.py"); + let folder_view = generated.module("windows__storage__i_storage_folder.pyi"); + let headers = + generated.module("windows__web__http__headers__http_content_header_collection.pyi"); + let properties = generated.module("windows__foundation__collections__property_set.pyi"); + let json = generated.module("windows__data__json__json_object.pyi"); + let accelerometer = generated.module("windows__devices__sensors__accelerometer.pyi"); + let compass = generated.module("windows__devices__sensors__compass.pyi"); + let dispatcher = generated.module("windows__system__dispatcher_queue.pyi"); + let xml = generated.module("windows__data__xml__dom__xml_document.pyi"); + + // Method, async, collection and property outputs are non-null by default. + assert_contains( + &folder, + "def create_file_async(self, desired_name: str) -> WinRTCoroutine[StorageFile]: ...", + ); + assert_contains( + &folder, + "def get_files_async(self) -> WinRTCoroutine[Sequence[StorageFile]]: ...", + ); + assert_contains( + &folder, + "def get_folder_from_path_async(path: str) -> WinRTCoroutine[StorageFolder]: ...", + ); + assert_contains( + &folder, + "def properties(self) -> StorageItemContentProperties: ...", + ); + assert_contains( + &json, + "def get_named_object(self, name: str) -> JsonObject: ...", + ); + assert_contains(&json, "def parse(input: str) -> JsonObject: ..."); + + // Try* results keep None on their result, async result and out values. + assert_contains( + &folder, + "def try_get_item_async(self, name: str) -> WinRTCoroutine[IStorageItem | None]: ...", + ); + assert_contains( + &json, + "def try_parse(input: str) -> tuple[JsonObject | None, bool]: ...", + ); + + // Members the Windows SDK documentation says can return null keep None, + // including through overloads, interfaces and async results. + assert_contains( + &accelerometer, + "def get_default() -> Accelerometer | None: ...", + ); + assert_contains( + &accelerometer, + "def get_default_with_accelerometer_reading_type(reading_type: 'AccelerometerReadingType') -> Accelerometer | None: ...", + ); + assert_contains(&compass, "def get_default() -> Compass | None: ..."); + assert_contains( + &dispatcher, + "def get_for_current_thread() -> DispatcherQueue | None: ...", + ); + assert_contains( + &dispatcher, + "def create_timer(self) -> DispatcherQueueTimer: ...", + ); + assert_contains( + &folder, + "def get_parent_async(self) -> WinRTCoroutine[StorageFolder | None]: ...", + ); + assert_contains( + &xml, + "def select_single_node(self, xpath: str) -> IXmlNode | None: ...", + ); + + // IReference values and Object values keep None, and so do properties + // whose documentation says a null value means "absent". + assert_contains(&headers, "def content_length(self) -> int | None: ..."); + assert_contains( + &headers, + "def content_type(self) -> HttpMediaTypeHeaderValue | None: ...", + ); + assert_contains( + &properties, + "def lookup(self, key: str) -> DynWinRTValue | None: ...", + ); + assert_contains( + &properties, + "def __getitem__(self, key: str) -> DynWinRTValue | None: ...", + ); + + // Inputs are unchanged. + assert_contains( + &headers, + "def content_length(self, value: int | None | IReference_UInt64) -> None: ...", + ); + assert_contains( + &folder, + "def create_folder_query(self, query_options: 'QueryOptionsLike') -> StorageFolderQueryResult: ...", + ); + assert_contains( + &properties, + "def __setitem__(self, key: str, value: DynWinRTValue | _DynWinRTObject | None) -> None: ...", + ); + + // Implementation protocols keep their obligations. + assert_contains(&folder_view, "class IStorageFolderHandlers(Protocol):"); + assert_contains( + &folder_view, + "def create_file_async_overload_default_options(self, desired_name: str) -> DynWinRTValue | None: ...", + ); + + // Runtime annotations stay pessimistic. + assert_contains( + &folder_py, + "def get_folder_from_path_async(path: str) -> WinRTCoroutine[StorageFolder | None]:", + ); + assert_contains( + &folder_py, + "def properties(self) -> StorageItemContentProperties | None:", + ); +} + +#[test] +fn collection_elements_follow_the_mutability_of_their_collection() { + let Some(generated) = Generated::new( + "elements", + "Windows.Storage.StorageFolder,Windows.Data.Json.JsonObject,\ + Windows.ApplicationModel.Resources.Core.ResourceMap,Windows.Media.Playback.MediaPlaybackList", + ) else { + return; + }; + let files = + generated.module("windows__foundation__collections__i_vector_view_storage_file.pyi"); + let array = generated.module("windows__data__json__json_array.pyi"); + let object = generated.module("windows__data__json__json_object.pyi"); + let resources = + generated.module("windows__application_model__resources__core__resource_map.pyi"); + let playlist = generated.module("windows__media__playback__media_playback_list.pyi"); + let observable = generated + .module("windows__foundation__collections__i_observable_vector_media_playback_item.pyi"); + + // Views keep non-null elements, including their item positions. + assert_contains(&files, "def get_at(self, index: int) -> StorageFile: ..."); + assert_contains( + &files, + "def __getitem__(self, index: int) -> StorageFile: ...", + ); + assert_contains( + &resources, + "class ResourceMap(_ResourceMapIdentity, Mapping[str, NamedResource], _DynWinRTRuntimeClass):", + ); + assert_contains( + &resources, + "def lookup(self, key: str) -> NamedResource: ...", + ); + + // Anyone can store null in a mutable collection, so its elements, item + // positions and element-reading members keep None. + assert_contains( + &array, + "class JsonArray(_JsonArrayIdentity, MutableSequence[IJsonValue | None], _DynWinRTRuntimeClass):", + ); + assert_contains( + &array, + "def get_at(self, index: int) -> IJsonValue | None: ...", + ); + assert_contains( + &array, + "def __getitem__(self, index: int) -> IJsonValue | None: ...", + ); + assert_contains( + &array, + "def get_object_at(self, index: int) -> JsonObject: ...", + ); + assert_contains( + &object, + "class JsonObject(_JsonObjectIdentity, MutableMapping[str, IJsonValue | None], _DynWinRTRuntimeClass):", + ); + assert_contains( + &object, + "def lookup(self, key: str) -> IJsonValue | None: ...", + ); + assert_contains( + &object, + "def get_named_array(self, name: str) -> JsonArray: ...", + ); + assert_contains( + &playlist, + "def items(self) -> MutableSequence[MediaPlaybackItem | None]: ...", + ); + assert_contains(&observable, "MutableSequence[MediaPlaybackItem | None]):"); + assert_contains( + &observable, + "def __getitem__(self, index: int) -> MediaPlaybackItem | None: ...", + ); +} diff --git a/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs b/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs index 739bda9b..01fbc7d6 100644 --- a/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs +++ b/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs @@ -331,7 +331,7 @@ fn python_class_self_and_base_markers_use_declarations_in_both_layouts() { "fixture must require a local self binding" ); assert!( - stub.contains("def echo_self(self, value: 'WidgetLike') -> Widget | None:"), + stub.contains("def echo_self(self, value: 'WidgetLike') -> Widget:"), "{stub}" ); assert!( @@ -375,13 +375,13 @@ fn python_class_self_and_base_markers_use_declarations_in_both_layouts() { def alpha(value: AlphaWidget, peer: BetaWidget, child: AlphaDerived) -> None: own: AlphaBaseLike = value foreign: BetaBaseLike = child - assert_type(value.echo_self(value), AlphaWidget | None) - assert_type(value.echo_foreign(peer), BetaWidget | None) + assert_type(value.echo_self(value), AlphaWidget) + assert_type(value.echo_foreign(peer), BetaWidget) def beta(value: BetaWidget, peer: AlphaWidget, child: BetaDerived) -> None: own: BetaBaseLike = value foreign: AlphaBaseLike = child - assert_type(value.echo_self(value), BetaWidget | None) - assert_type(value.echo_foreign(peer), AlphaWidget | None) + assert_type(value.echo_self(value), BetaWidget) + assert_type(value.echo_foreign(peer), AlphaWidget) "#, ); support(&package, &[]); @@ -587,13 +587,13 @@ fn python_named_class_closed_generic_collision_uses_allocated_declaration() { from typing import assert_type assert_type(Named(), Named) assert_type(Named(7), Named) -assert_type(Named.get_current(), Named | None) +assert_type(Named.get_current(), Named) def check(named: Named, generic: Bucket, derived: Derived) -> None: base: NamedLike = derived - assert_type(named.echo_self(named), Named | None) - assert_type(named.echo_generic(generic), Bucket | None) - assert_type(generic.echo_self(generic), Bucket | None) - assert_type(generic.echo_named(named), Named | None) + assert_type(named.echo_self(named), Named) + assert_type(named.echo_generic(generic), Bucket) + assert_type(generic.echo_self(generic), Bucket) + assert_type(generic.echo_named(named), Named) "#, ), ); @@ -2172,8 +2172,8 @@ fn python_cross_role_class_owners_keep_self_bindings_and_qualified_helpers() { r#"{imports} from typing import assert_type def check(value: Owner) -> None: - assert_type(value.echo_self(value), Owner | None) - assert_type(value.get_self(), Owner | None) + assert_type(value.echo_self(value), Owner) + assert_type(value.get_self(), Owner) assert_type(value.echo_payload(URLValue(17)), URLValue) "# ), @@ -2705,9 +2705,9 @@ fn python_enum_closed_generic_collision_preserves_projection_and_native_identity from typing import assert_type assert_type(RootKind(0), Kind) def check(local: IUse, foreign: IForeign) -> None: - assert_type(local.get_bucket(), Bucket | None) - assert_type(local.get_bucket(), RootBucket | None) - assert_type(foreign.get_bucket(), Bucket | None) + assert_type(local.get_bucket(), Bucket) + assert_type(local.get_bucket(), RootBucket) + assert_type(foreign.get_bucket(), Bucket) assert_type(local.echo_kind(Kind.Unknown), Kind) assert_type(foreign.echo_kind(Kind.Unknown), Kind) assert_type(local.echo_kinds([Kind.Unknown]), list[Kind]) @@ -2911,10 +2911,10 @@ fn python_class_companion_aliases_preserve_cli_and_standalone_contracts() { r#"{imports} from typing import assert_type def check(owner: Widget, peer: Peer, use: IUse) -> None: - assert_type(owner.echo(peer), Peer | None) - assert_type(peer.echo(owner), Widget | None) - assert_type(use.echo_owner(owner), Widget | None) - assert_type(use.echo_peer(peer), Peer | None) + assert_type(owner.echo(peer), Peer) + assert_type(peer.echo(owner), Widget) + assert_type(use.echo_owner(owner), Widget) + assert_type(use.echo_peer(peer), Peer) "#, ), ); diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi index ae2e8fc3..4194cea1 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi @@ -19,25 +19,25 @@ IID_IIterator_IWwwFormUrlDecoderEntry: WinGUID class _IIterator_IWwwFormUrlDecoderEntryIdentity(Protocol): def _dynwinrt_iid_g2bb0b33cef11eb455ace3197fce2b56f216f6802e23b208c88ebad8f950f8628(self) -> None: ... -class IIterator_IWwwFormUrlDecoderEntry(_IIterator_IWwwFormUrlDecoderEntryIdentity, Iterator[IWwwFormUrlDecoderEntry | None]): +class IIterator_IWwwFormUrlDecoderEntry(_IIterator_IWwwFormUrlDecoderEntryIdentity, Iterator[IWwwFormUrlDecoderEntry]): @builtins.property def _obj(self) -> DynWinRTValue: ... # Windows.Foundation.Collections.IIterator_IWwwFormUrlDecoderEntry cannot be implemented: generic interface implementations are not supported def __init__(self, obj: DynWinRTValue) -> None: ... - def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry | None]: ... - def __next__(self) -> IWwwFormUrlDecoderEntry | None: ... + def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... + def __next__(self) -> IWwwFormUrlDecoderEntry: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... @builtins.property - def current(self) -> IWwwFormUrlDecoderEntry | None: ... + def current(self) -> IWwwFormUrlDecoderEntry: ... @builtins.property def has_current(self) -> bool: ... def move_next(self) -> bool: ... - def get_many(self, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi index 815ffece..728dceb6 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi @@ -60,7 +60,7 @@ class UriLike(_UriIdentity, Protocol): def query(self) -> str: ... @builtins.property - def query_parsed(self) -> WwwFormUrlDecoder | None: ... + def query_parsed(self) -> WwwFormUrlDecoder: ... @builtins.property def raw_uri(self) -> str: ... @@ -79,7 +79,7 @@ class UriLike(_UriIdentity, Protocol): def equals(self, p_uri: 'UriLike') -> bool: ... - def combine_uri(self, relative_uri: str) -> Uri | None: ... + def combine_uri(self, relative_uri: str) -> Uri: ... @builtins.property def absolute_canonical_uri(self) -> str: ... diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi index 46688389..c223db0a 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi @@ -34,48 +34,48 @@ class WwwFormUrlDecoderLike(_WwwFormUrlDecoderIdentity, Protocol): def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... def get_first_value_by_name(self, name: str) -> str: ... @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... -class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderEntry | None], _DynWinRTRuntimeClass): +class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderEntry], _DynWinRTRuntimeClass): def __init__(self, query: str) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... def get_first_value_by_name(self, name: str) -> str: ... @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... @@ -83,16 +83,16 @@ class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderE def create_www_form_url_decoder(query: str) -> 'WwwFormUrlDecoder': ... -class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry | None]): +class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry]): def __init__(self, obj: DynWinRTValue) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... @@ -101,22 +101,22 @@ class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry | Non @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... -class IIterable_IWwwFormUrlDecoderEntry(Iterable[IWwwFormUrlDecoderEntry | None]): +class IIterable_IWwwFormUrlDecoderEntry(Iterable[IWwwFormUrlDecoderEntry]): def __init__(self, obj: DynWinRTValue) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... - def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry | None]: ... + def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ...