diff --git a/src/Tests/FunctionalTests/ClassActivation/Program.cs b/src/Tests/FunctionalTests/ClassActivation/Program.cs index 69b6db59a..423a0cf3a 100644 --- a/src/Tests/FunctionalTests/ClassActivation/Program.cs +++ b/src/Tests/FunctionalTests/ClassActivation/Program.cs @@ -1,8 +1,17 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using System.Windows.Input; using TestComponent; using TestComponentCSharp; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Foundation.Tasks; using WindowsRuntime.InteropServices; CustomDisposableTest customDisposableTest = new(); @@ -38,8 +47,6 @@ { void* testMixedComClassUnknownPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(testMixedComClass); void* classicComActionPtr = null; - void* closablePtr = null; - void* inspectablePtr = null; try { @@ -53,21 +60,101 @@ Marshal.ThrowExceptionForHR(((delegate* unmanaged[MemberFunction])(*(void***)classicComActionPtr)[3])(classicComActionPtr)); // Sanity check: we should still also be able to 'QueryInterface' for other interfaces - Marshal.ThrowExceptionForHR(Marshal.QueryInterface( - pUnk: (nint)testMixedComClassUnknownPtr, - iid: new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), - ppv: out *(nint*)&closablePtr)); - Marshal.ThrowExceptionForHR(Marshal.QueryInterface( - pUnk: (nint)testMixedComClassUnknownPtr, - iid: new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90"), - ppv: out *(nint*)&inspectablePtr)); + ComHelpers.EnsureQueryInterface( + unknownPtr: testMixedComClassUnknownPtr, + iids: [ + new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), + new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90")]); } finally { WindowsRuntimeMarshal.Free(testMixedComClassUnknownPtr); WindowsRuntimeMarshal.Free(classicComActionPtr); - WindowsRuntimeMarshal.Free(closablePtr); - WindowsRuntimeMarshal.Free(inspectablePtr); + } +} + +ConstructedDerivedType constructedDerivedType = new(); + +unsafe +{ + void* constructedDerivedTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(constructedDerivedType); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: constructedDerivedTypePtr, + iids: [ + new Guid("E2FCC7C1-3BFC-5A0B-B2B0-72E769D1CB7E"), // 'IEnumerable' + new Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F"), // 'IEnumerable' + new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), // 'IClosable' + new Guid("8311ED02-4F46-5CAF-BF01-2AE354C04BF5")]); // 'IMapChangedEventArgs' + } + finally + { + WindowsRuntimeMarshal.Free(constructedDerivedTypePtr); + } +} + +object genericType = GenericFactory.Make(); + +unsafe +{ + void* genericTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(genericType); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: genericTypePtr, + iids: [ + new Guid("30160817-1D7D-54E9-99DB-D7636266A476"), // 'IEnumerable' + new Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F"), // 'IEnumerable' + new Guid("907661AB-C065-5A14-9AC3-2FEB0D164DDA"), // 'IReadOnlyDictionary' + new Guid("3631E370-2F65-5F4A-8364-1619C536DB12"), // 'IEnumerable>' + new Guid("F61E8483-D7A0-5840-9DCF-40423CCC97D0")]); // 'IMapChangedEventArgs' + } + finally + { + WindowsRuntimeMarshal.Free(genericTypePtr); + } +} + +IAsyncActionWithProgress asyncActionWithProgress = GenericFactory.MakeAsyncActionWithProgress(); + +unsafe +{ + void* asyncActionWithProgressPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncActionWithProgress); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: asyncActionWithProgressPtr, + iids: [ + new Guid("0EDE398F-0090-574E-AD30-E152B433BF6A"), // 'IAsyncActionWithProgress' + new Guid("00000036-0000-0000-C000-000000000046")]); // 'IAsyncInfo' + } + finally + { + WindowsRuntimeMarshal.Free(asyncActionWithProgressPtr); + } +} + +IAsyncOperation asyncOperation = GenericFactory.MakeAsyncOperation(); + +unsafe +{ + void* asyncOperationPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncOperation); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: asyncOperationPtr, + iids: [ + new Guid("1AE01209-1ACA-51D3-A080-8B1214E0A39E"), // 'IAsyncOperation' + new Guid("00000036-0000-0000-C000-000000000046")]); // 'IAsyncInfo' + } + finally + { + WindowsRuntimeMarshal.Free(asyncOperationPtr); } } @@ -86,6 +173,102 @@ public void Dispose() } } +class GenericBaseType : IEnumerable, IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} + +// This type is extending a generic type. The base type is a 'TypeSpec' with a constructed +// generic type. We use this to validate that the base constructed interfaces are seen. +class ConstructedDerivedType : GenericBaseType, IMapChangedEventArgs +{ + public CollectionChange CollectionChange => throw new NotImplementedException(); + + public IEnumerable Key => throw new NotImplementedException(); +} + +class GenericType : IEnumerable, IReadOnlyDictionary, IMapChangedEventArgs +{ + public T2 this[T1 key] => throw new NotImplementedException(); + + public IEnumerable Keys => throw new NotImplementedException(); + + public IEnumerable Values => throw new NotImplementedException(); + + public int Count => throw new NotImplementedException(); + + public CollectionChange CollectionChange => throw new NotImplementedException(); + + public T2 Key => throw new NotImplementedException(); + + public bool ContainsKey(T1 key) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public bool TryGetValue(T1 key, [MaybeNullWhen(false)] out T2 value) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + throw new NotImplementedException(); + } +} + +class GenericFactory +{ + // This method is caling a generic one, which then constructs a generic type. + // The 'GenericType' instantiation doesn't appear anywhere in the .dll, + // but we should be able to still find it, because: + // - We can see 'Make' in the 'MethodSpec' table. + // - We can inspect all instructions in that constructed method + // - We can see a 'newobj' instruction with a 'MemberRef' to 'GenericType.ctor' + // From there, we should be able to gather info on that constructed generic type. + public static object Make() => Make(); + + private static object Make() => new GenericType(); + + // Specific test for 'AsyncInfo.Run' with explicit type arguments + [SupportedOSPlatform("windows10.0.10240.0")] + public static IAsyncActionWithProgress MakeAsyncActionWithProgress() + { + return AsyncInfo.Run((token, progress) => Task.CompletedTask); + } + + // Specific test for 'AsyncInfo.Run' with transitive type arguments. + // Note: transitive type arguments aren't currently supported for this. + [SupportedOSPlatform("windows10.0.10240.0")] + public static IAsyncOperation MakeAsyncOperation() + { + return AsyncInfo.Run(token => Task.FromResult(default(TimeSpan))); + } +} + [Guid("3C832AA5-5F7E-46EE-B1BF-7FE03AE866AF")] [GeneratedComInterface] partial interface IClassicComAction @@ -93,6 +276,35 @@ partial interface IClassicComAction void Invoke(); } +file static class ComHelpers +{ + [SupportedOSPlatform("windows6.3")] + public static unsafe void EnsureQueryInterface(void* unknownPtr, params ReadOnlySpan iids) + { + foreach (Guid iid in iids) + { + int hresult = Marshal.QueryInterface( + pUnk: (nint)unknownPtr, + iid: iid, + ppv: out nint interfacePtr); + + WindowsRuntimeMarshal.Free((void*)interfacePtr); + + const int E_NOINTERFACE = unchecked((int)0x80004002); + + // If we failed due to 'E_NOINTERFACE', we want a custom message with the IID, to help debugging + if (hresult == E_NOINTERFACE) + { + throw new InvalidCastException($"Specified cast is not valid (IID: '{iid.ToString().ToUpperInvariant()}')."); + } + else + { + Marshal.ThrowExceptionForHR(hresult); + } + } + } +} + /* // new RCW / Factory activation var instance = new Class(); diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs index 215b6a7cc..d70160642 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs @@ -486,6 +486,9 @@ public static void ImplType( implType.Methods.Add(makeVectorChangedMethod); implType.Methods.Add(get_VectorChangedTableMethod); implType.Properties.Add(vectorChangedTableProperty); + + // Track the type (it may be needed by COM interface entries for user-defined types) + emitState.TrackTypeDefinition(implType, vectorType, "Impl"); } /// diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs new file mode 100644 index 000000000..ac66c8781 --- /dev/null +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using WindowsRuntime.InteropGenerator.Visitors; + +namespace WindowsRuntime.InteropGenerator.Discovery; + +/// +internal partial class InteropTypeDiscovery +{ + /// + /// Tries to track a constructed generic type. + /// + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + public static void TryTrackGenericTypeInstance( + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Filter all constructed generic type signatures we have. We don't care about generic type + // definitions (eg. 'TypedEventHandler`1') for the purposes of marshalling code. + if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) + { + return; + } + + // Ignore types that are not fully resolvable (this likely means a .dll is missing) + if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition)) + { + // Log a warning the first time we fail to resolve this generic instantiation in this module + if (discoveryState.TrackFailedResolutionType(typeSignature, module)) + { + WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + return; + } + + // If the current type signature represents a Windows Runtime type, track it + if (typeSignature.IsWindowsRuntimeType(interopReferences)) + { + TryTrackWindowsRuntimeGenericTypeInstance( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences, + module); + } + else + { + // Otherwise, try to track information for some constructed managed type + TryTrackManagedGenericTypeInstance( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences); + } + } + + /// + /// Tries to track an SZ array type. + /// + /// The for the SZ array type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + public static void TryTrackSzArrayType( + SzArrayTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Filter all constructed generic type signatures we have. We don't care about + // generic type definitions (eg. '!0[]') for the purposes of marshalling code. + if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) + { + return; + } + + // Ignore types that are not fully resolvable (this likely means a .dll is missing) + if (!typeSignature.IsFullyResolvable(out _)) + { + // Log a warning the first time we fail to resolve this SZ array in this module + if (discoveryState.TrackFailedResolutionType(typeSignature, module)) + { + WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + return; + } + + // Ignore array types that are not Windows Runtime types + if (!typeSignature.IsWindowsRuntimeType(interopReferences)) + { + return; + } + + // Track all SZ array types, as we'll need to emit marshalling code for them + discoveryState.TrackSzArrayType(typeSignature); + } + + /// + /// Tries to track a constructed generic Windows Runtime type. + /// + /// The for the type to analyze. + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + private static void TryTrackWindowsRuntimeGenericTypeInstance( + TypeDefinition typeDefinition, + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Gather all 'KeyValuePair<,>' instances + if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences)) + { + discoveryState.TrackKeyValuePairType(typeSignature); + + return; + } + + // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus + // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler'). + // The filtering is already done above, so here we can rely the type will be of one of those kinds. + if (typeDefinition.IsDelegate) + { + discoveryState.TrackGenericDelegateType(typeSignature); + + return; + } + + // Track all projected Windows Runtime generic interfaces + if (typeDefinition.IsInterface) + { + discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); + + // We also want to crawl base interfaces + foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) + { + // Filter out just constructed generic interfaces, since we only care about those here. + // The non-generic ones are only useful when gathering interfaces for user-defined types. + if (interfaceSignature is not GenericInstanceTypeSignature constructedSignature) + { + continue; + } + + if (!interfaceSignature.IsFullyResolvable(out _)) + { + // Also log a warning the first time we fail to resolve one of the recursively discovered generic + // instantiations from this module. The enumeration also yields back interfaces that couldn't be + // resolved, as that step is performed after yielding. This is done so we can have our own logic + // to log warnings or throw errors from here while we're processing interfaces in this module. + if (discoveryState.TrackFailedResolutionType(interfaceSignature, module)) + { + WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + continue; + } + + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + } + } + } + + /// + /// Tries to track a constructed generic user-defined type. + /// + /// The for the type to analyze. + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + private static void TryTrackManagedGenericTypeInstance( + TypeDefinition typeDefinition, + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences) + { + // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types. + // This is because "pass-array" and "fill-array" parameters are projected using spans, but + // those projections require the marshalling code produced when discovering SZ array types. + // So if we see any of these spans where the element type is a Windows Runtime type, we + // manually construct an SZ array type for it and add it to the set of tracked array types. + if (typeSignature.IsValueType && + typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) && + typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences)) + { + discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType()); + + return; + } + + // Otherwise, try to track a constructed user-defined type + TryTrackExposedUserDefinedType( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences); + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs new file mode 100644 index 000000000..503393287 --- /dev/null +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.Models; +using WindowsRuntime.InteropGenerator.References; + +namespace WindowsRuntime.InteropGenerator.Discovery; + +/// +/// A discovery helper type for interop types. +/// +internal static partial class InteropTypeDiscovery +{ + /// + /// A thread-local instance that can be reused by discovery logic. + /// + [ThreadStatic] + private static TypeSignatureEquatableSet.Builder? TypeSignatures; + + /// + /// Tries to track a given composable Windows Runtime type. + /// + /// The for the type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + public static void TryTrackTypeHierarchyType( + TypeDefinition typeDefinition, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState) + { + // We only care about projected Windows Runtime classes + if (!typeDefinition.IsProjectedWindowsRuntimeClassType) + { + return; + } + + // Ignore types that don't have another base class + if (!typeDefinition.HasBaseType(out ITypeDefOrRef? baseType)) + { + return; + } + + // We need to resolve the base type to be able to look up attributes on it + if (!baseType.IsFullyResolvable(out _)) + { + WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + return; + } + + // If the base type is also a projected Windows Runtime type, track it + if (baseType.IsProjectedWindowsRuntimeType) + { + discoveryState.TrackTypeHierarchyEntry(typeDefinition.FullName, baseType.FullName); + } + } + + /// + /// Tries to track an exposed user-defined type. + /// + /// The for the type to analyze. + /// The for the type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// + /// This method expects to either be non-generic, or + /// to have be a fully constructed signature for it. + /// + public static void TryTrackExposedUserDefinedType( + TypeDefinition typeDefinition, + TypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences) + { + // Ignore all type definitions with generic parameters where we don't have constructed + // generic type signature. We can track these separately when we see them as instantiated. + if (typeDefinition.HasGenericParameters && typeSignature is not GenericInstanceTypeSignature) + { + return; + } + + // We can skip all projected Windows Runtime types early, as they don't need CCW support + if (typeDefinition.IsProjectedWindowsRuntimeType) + { + return; + } + + // We'll need to look up attributes and enumerate interfaces across the entire type + // hierarchy for this type, so make sure that we can resolve all types from it first. + if (!typeDefinition.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType)) + { + WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + return; + } + + // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime + if (!typeDefinition.IsPossiblyWindowsRuntimeExposedType || typeDefinition.IsWindowsRuntimeManagedOnlyType(interopReferences)) + { + return; + } + + // Reuse the thread-local builder to track all implemented interfaces for the current type + TypeSignatureEquatableSet.Builder interfaces = TypeSignatures ??= new TypeSignatureEquatableSet.Builder(); + + // Since we're reusing the builder for all types, make sure to clear it first + interfaces.Clear(); + + // We want to explicitly track whether the type implements any projected Windows Runtime + // interfaces, as we are only interested in such types. We want to also gather all + // implemented '[GeneratedComInterface]' interfaces, but if a type only implements + // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly. + bool hasAnyProjectedWindowsRuntimeInterfaces = false; + + // Gather all implemented Windows Runtime interfaces for the current type + foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) + { + // Make sure we can resolve the interface type fully, which we should always be able to do. + // This can really only fail for some constructed generics, for invalid type arguments. + if (!interfaceSignature.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) + { + WellKnownInteropExceptions.InterfaceImplementationTypeNotResolvedWarning(interfaceSignature, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Check for projected Windows Runtime interfaces first + if (interfaceSignature.IsWindowsRuntimeType(interopReferences)) + { + hasAnyProjectedWindowsRuntimeInterfaces = true; + + interfaces.Add(interfaceSignature); + + // If the current interface is generic, also make sure that it's tracked. This is needed + // to fully cover all possible constructed generic interface types that might be needed. + // For instance, consider this case: + // + // class A : IEnumerable; + // class B : A; + // + // While processing 'B', we'll discover the constructed 'IEnumerable' interface. + // This interface would not have been discovered when processing 'A', as it's not + // in the 'TypeSpec' metadata table, and only appears as unconstructed on 'A'. + // So the discovery logic for generic instantiations below would otherwise miss it. + if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) + { + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + } + } + else if (interfaceDefinition.IsGeneratedComInterfaceType) + { + // We can only gather this type if we can find the generated 'InterfaceInformation' type. + // If we can't find it, we can't add the interface to the list of interface entries. We + // should warn if that's the (unlikely) case, so users can at least know that something + // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures. + if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _)) + { + WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time + // so we can check against some specific IID which might affect how we construct the COM + // interface entries. For instance, we need to check whether 'IMarshal' is implemented. + if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid)) + { + WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Validate that the current interface isn't trying to implement a reserved interface. + // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'. + if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName)) + { + throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, typeDefinition, iid, interfaceName); + } + + // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below) + interfaces.Add(interfaceSignature); + } + } + + // If the user-defined type implements at least a Windows Runtime interface, then it's considered exposed. + // We don't want to handle marshalling code for types with only '[GeneratedComInterface]' interfaces. + if (hasAnyProjectedWindowsRuntimeInterfaces) + { + discoveryState.TrackUserDefinedType(typeSignature, interfaces.ToEquatableSet()); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs index 42357863e..50d273efe 100644 --- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs +++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs @@ -430,11 +430,11 @@ public static WellKnownInteropException CustomMappedTypeComWrappersMarshallerAtt } /// - /// Failed to resolve a '[GeneratedComInterface]' type. + /// Failed to resolve the type of an implemented interface. /// - public static WellKnownInteropWarning GeneratedComInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) + public static WellKnownInteropWarning InterfaceImplementationTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) { - return Warning(49, $"Failed to resolve the '[GeneratedComInterface]' type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); + return Warning(49, $"Failed to resolve interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); } /// @@ -534,11 +534,11 @@ public static WellKnownInteropWarning GeneratedComInterfaceGuidAttributeNotFound } /// - /// Failed to resolve a Windows Runtime interface type. + /// Failed to resolve a base type for a user-defined type. /// - public static WellKnownInteropWarning WindowsRuntimeInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) + public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type) { - return Warning(62, $"Failed to resolve the Windows Runtime interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); + return Warning(62, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated."); } /// @@ -573,14 +573,6 @@ public static WellKnownInteropWarning WindowsRuntimeClassTypeNotResolvedWarning( return Warning(66, $"Failed to resolve the base type '{baseType}' for Windows Runtime class type '{classType}': runtime casts to the base type will not work if the type is trimmed."); } - /// - /// Failed to resolve a base type for a user-defined type. - /// - public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type) - { - return Warning(67, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated."); - } - /// /// Creates a new exception with the specified id and message. /// diff --git a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs index ac8ffeaac..aeee73acb 100644 --- a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs @@ -8,6 +8,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Helpers; using WindowsRuntime.InteropGenerator.Visitors; @@ -230,6 +231,58 @@ static IEnumerable EnumerateTypeSignatures( yield return result; } } + + IReadOnlyList instructions = specification.Method!.Resolve()?.CilMethodBody?.Instructions ?? (IReadOnlyList)[]; + + // Go through instruction to look for new objects + foreach (CilInstruction instruction in instructions) + { + // We only care for 'newobj' instructions + if (instruction.OpCode != CilOpCodes.Newobj) + { + continue; + } + + // Check that we can retrieve the target object type + if (instruction.Operand is not IMethodDefOrRef { DeclaringType: ITypeDefOrRef objectType }) + { + continue; + } + + // Instantiate the object type and enumerate all signatures + foreach (TResult result in EnumerateTypeSignatures( + objectType.ToTypeSignature().InstantiateGenericTypes(genericContext), + results, + visitor)) + { + yield return result; + } + } + + // Go through instruction to look for new arrays + foreach (CilInstruction instruction in instructions) + { + // We only care for 'newarr' instructions + if (instruction.OpCode != CilOpCodes.Newarr) + { + continue; + } + + // Check that we can retrieve the target object type + if (instruction.Operand is not ITypeDefOrRef arrayType) + { + continue; + } + + // Instantiate the object type and enumerate all signatures + foreach (TResult result in EnumerateTypeSignatures( + arrayType.ToTypeSignature().InstantiateGenericTypes(genericContext), + results, + visitor)) + { + yield return result; + } + } } } diff --git a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs index 515518250..e831a8e68 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs @@ -25,6 +25,17 @@ internal static class TypeDefinitionExtensions /// public bool IsStatic => type.IsAbstract && type.IsSealed; + /// + /// Gets whether a given type has a base type other than . + /// + /// The resulting base type, if available. + public bool HasBaseType([NotNullWhen(true)] out ITypeDefOrRef? baseType) + { + baseType = type.BaseType; + + return baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object }); + } + /// /// Gets a value indicating whether a given 's type hierarchy can be fully resolved to type definitions. /// @@ -32,18 +43,16 @@ internal static class TypeDefinitionExtensions /// Whether the input 's type hierarchy can be fully resolved to type definitions. public bool IsTypeHierarchyFullyResolvable([NotNullWhen(false)] out ITypeDefOrRef? failedResolutionBaseType) { - ITypeDefOrRef? baseType = type.BaseType; + TypeDefinition currentDefinition = type; - while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object })) + while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType)) { - if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) + if (!baseType.IsFullyResolvable(out currentDefinition!)) { failedResolutionBaseType = baseType; return false; } - - baseType = baseDefinition.BaseType; } failedResolutionBaseType = null; @@ -224,38 +233,18 @@ public IEnumerable EnumerateBaseTypesAndSelf() { yield return type; - ITypeDefOrRef? baseType = type.BaseType; + TypeDefinition currentDefinition = type; - while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object })) + while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType)) { // If we can't resolve the current base type, we have to stop. // Callers should validate the type hierarchy before calling this. - if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) + if (!baseType.IsFullyResolvable(out currentDefinition!)) { yield break; } - yield return baseDefinition; - - baseType = baseDefinition.BaseType; - } - } - - /// - /// Enumerates all interface types implementation by the specified type, including those implemented by base types. - /// - /// The sequence of interface types implemented by the input type. - /// - /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy. - /// - public IEnumerable EnumerateAllInterfaces() - { - foreach (TypeDefinition currentType in type.EnumerateBaseTypesAndSelf()) - { - foreach (InterfaceImplementation implementation in currentType.Interfaces) - { - yield return implementation; - } + yield return currentDefinition; } } diff --git a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs index be76c72f1..ca7b32cd0 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Metadata.Tables; namespace WindowsRuntime.InteropGenerator; @@ -43,5 +45,65 @@ public bool IsFullyResolvable([NotNullWhen(true)] out TypeDefinition? definition return true; } + + /// + /// Enumerates all interface types implemented by the specified type, including those implemented by base types. + /// + /// The sequence of interface types implemented by the input type. + /// + /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy. + /// + public IEnumerable EnumerateAllInterfaces() + { + TypeSignature currentSignature = signature; + + while (currentSignature is not null) + { + // If we can't resolve the current type signature, we have to stop. + // Callers should validate the type hierarchy before calling this. + if (!currentSignature.IsFullyResolvable(out TypeDefinition? currentDefinition)) + { + yield break; + } + + GenericContext context = new(currentSignature as GenericInstanceTypeSignature, null); + + // Go over all interfaces implemented on the current type. We don't need + // to recurse on them, as classes always declare the full transitive set. + foreach (InterfaceImplementation interfaceImplementation in currentDefinition.Interfaces) + { + // Ignore this interface if we can't actually retrieve the interface type. + // This should never happen for valid .NET assemblies, but just in case. + if (interfaceImplementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature) + { + continue; + } + + // Return either the current non-generic interface, or the constructed generic one. + // We don't have to check: if the interface is not generic, this will be a no-op. + yield return interfaceSignature.InstantiateGenericTypes(context); + + // Also recurse on the base interfaces (no need to instantiate the returned interface type + // signatures for base interfaces here: they will be already instantiated when returned). + foreach (TypeSignature baseInterface in interfaceSignature.EnumerateAllInterfaces()) + { + yield return baseInterface; + } + } + + ITypeDefOrRef? baseType = currentDefinition.BaseType; + + // Stop if we have no available base type or if we reached 'object' + if (baseType is null or CorLibTypeSignature { ElementType: ElementType.Object }) + { + yield break; + } + + // Get the signature for the base type, adding back any generic context. + // Note that the base type will always be a reference type, even for + // struct types (in that case, the base type will be 'System.ValueType'). + currentSignature = baseType.ToReferenceTypeSignature().InstantiateGenericTypes(context); + } + } } } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs index fd06e8ddd..bbd658e80 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs @@ -302,7 +302,6 @@ public static MethodDefinition GetResults( { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, { Stloc_1 }, { Ldloc_1 }, - { Ldarg_1 }, { Ldloca_S, loc_2_resultNative }, { Ldloc_1 }, { Ldind_I }, diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs index a33b21bd9..8f688bbd3 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs @@ -133,8 +133,8 @@ public static void RewriteMethod( IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef( name: "ConvertToUnmanaged"u8, signature: MethodSignature.CreateStatic( - returnType: retValType, - parameterTypes: [retValType.GetAbiType(interopReferences)])); + returnType: retValType.GetAbiType(interopReferences), + parameterTypes: [retValType])); // Delegate to the marshaller to convert the managed value type on the evaluation stack body.Instructions.ReplaceRange(marker, [ diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index a417b9d33..688968d69 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -9,11 +9,11 @@ using System.Threading.Tasks; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Discovery; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Models; using WindowsRuntime.InteropGenerator.References; using WindowsRuntime.InteropGenerator.Resolvers; -using WindowsRuntime.InteropGenerator.Visitors; namespace WindowsRuntime.InteropGenerator.Generation; @@ -155,31 +155,7 @@ private static void DiscoverTypeHierarchyTypes( { args.Token.ThrowIfCancellationRequested(); - // We only care about projected Windows Runtime classes - if (!type.IsProjectedWindowsRuntimeClassType) - { - continue; - } - - // Ignore types that don't have another base class - if (type.BaseType is null || SignatureComparer.IgnoreVersion.Equals(type.BaseType, module.CorLibTypeFactory.Object)) - { - continue; - } - - // We need to resolve the base type to be able to look up attributes on it - if (!type.BaseType.IsFullyResolvable(out TypeDefinition? baseType)) - { - WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(type.BaseType, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // If the base type is also a projected Windows Runtime type, track it - if (baseType.IsProjectedWindowsRuntimeType) - { - discoveryState.TrackTypeHierarchyEntry(type.FullName, baseType.FullName); - } + InteropTypeDiscovery.TryTrackTypeHierarchyType(type, args, discoveryState); } } catch (Exception e) @@ -210,120 +186,13 @@ private static void DiscoverExposedUserDefinedTypes( { args.Token.ThrowIfCancellationRequested(); - // Ignore all type definitions with generic parameters, because they would be - // unconstructed (by definition). We'll process instantiations that we can see - // separately in the discovery phase, same as we do for constructed interfaces. - if (type.HasGenericParameters) - { - continue; - } - - // We can skip all projected Windows Runtime types early, as they don't need CCW support - if (type.IsProjectedWindowsRuntimeType) - { - continue; - } - - // We'll need to look up attributes and enumerate interfaces across the entire type - // hierarchy for this type, so make sure that we can resolve all types from it first. - if (!type.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType)) - { - WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime - if (!type.IsPossiblyWindowsRuntimeExposedType || type.IsWindowsRuntimeManagedOnlyType(interopReferences)) - { - continue; - } - - // Since we're reusing the builder for all types, make sure to clear it first - interfaces.Clear(); - - // We want to explicitly track whether the type implements any projected Windows Runtime - // interfaces, as we are only interested in such types. We want to also gather all - // implemented '[GeneratedComInterface]' interfaces, but if a type only implements - // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly. - bool hasAnyProjectedWindowsRuntimeInterfaces = false; - - // Gather all implemented Windows Runtime interfaces for the current type - foreach (InterfaceImplementation implementation in type.EnumerateAllInterfaces()) - { - // If the current implementation has no valid interface, skip it. - // This should never really happen for valid .NET assemblies. - if (implementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature) - { - continue; - } - - // Check for projected Windows Runtime interfaces first - if (interfaceSignature.IsWindowsRuntimeType(interopReferences)) - { - // Make sure we can resolve the interface type fully, which we should always be able to do. - // This can really only fail for some constructed generics, for invalid type arguments. - if (!interfaceSignature.IsFullyResolvable(out _)) - { - WellKnownInteropExceptions.WindowsRuntimeInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - hasAnyProjectedWindowsRuntimeInterfaces = true; - - interfaces.Add(interfaceSignature); - } - else if (implementation.Interface.IsGeneratedComInterfaceType) - { - // To properly track '[GeneratedComInterface]' implementations, we need to be able to resolve those interface types - if (!implementation.Interface.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) - { - WellKnownInteropExceptions.GeneratedComInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // We can only gather this type if we can find the generated 'InterfaceInformation' type. - // If we can't find it, we can't add the interface to the list of interface entries. We - // should warn if that's the (unlikely) case, so users can at least know that something - // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures. - if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _)) - { - WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time - // so we can check against some specific IID which might affect how we construct the COM - // interface entries. For instance, we need to check whether 'IMarshal' is implemented. - if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid)) - { - WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // Validate that the current interface isn't trying to implement a reserved interface. - // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'. - if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName)) - { - throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, type, iid, interfaceName); - } - - // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below) - interfaces.Add(interfaceSignature); - } - } - - // If the user-defined type doesn't implement any Windows Runtime interfaces, it's not considered exposed - if (!hasAnyProjectedWindowsRuntimeInterfaces) - { - continue; - } - - discoveryState.TrackUserDefinedType(type.ToTypeSignature(), interfaces.ToEquatableSet()); + // Track the type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackExposedUserDefinedType( + typeDefinition: type, + typeSignature: type.ToTypeSignature(), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences); } } catch (Exception e) @@ -351,92 +220,13 @@ private static void DiscoverGenericTypeInstantiations( { args.Token.ThrowIfCancellationRequested(); - // Filter all constructed generic type signatures we have. We don't care about generic type - // definitions (eg. 'TypedEventHandler`1') for the purposes of marshalling code. - if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) - { - continue; - } - - // Ignore types that are not fully resolvable (this likely means a .dll is missing) - if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition)) - { - // Log a warning the first time we fail to resolve this generic instantiation in this module - if (discoveryState.TrackFailedResolutionType(typeSignature, module)) - { - WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types. - // This is because "pass-array" and "fill-array" parameters are projected using spans, but - // those projections require the marshalling code produced when discovering SZ array types. - // So if we see any of these spans where the element type is a Windows Runtime type, we - // manually construct an SZ array type for it and add it to the set of tracked array types. - if (typeSignature.IsValueType && - typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) && - typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences)) - { - discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType()); - - continue; - } - - // Ignore generic instantiations that are not Windows Runtime types. That is, those that - // have a generic type definition that's not a Windows Runtime type, or that have any type - // arguments that are not Windows Runtime types. - if (!typeSignature.IsWindowsRuntimeType(interopReferences)) - { - continue; - } - - // Gather all 'KeyValuePair<,>' instances - if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences)) - { - discoveryState.TrackKeyValuePairType(typeSignature); - - continue; - } - - // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus - // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler'). - // The filtering is already done above, so here we can rely the type will be of one of those kinds. - if (typeDefinition.IsDelegate) - { - discoveryState.TrackGenericDelegateType(typeSignature); - - continue; - } - - // Track all projected Windows Runtime generic interfaces - if (typeDefinition.IsInterface) - { - discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); - - // We also want to crawl base interfaces - foreach (GenericInstanceTypeSignature interfaceSignature in typeDefinition.EnumerateGenericInstanceInterfaceSignatures(typeSignature)) - { - if (!interfaceSignature.IsFullyResolvable(out _)) - { - // Also log a warning the first time we fail to resolve one of the recursively discovered generic - // instantiations from this module. The enumeration also yields back interfaces that couldn't be - // resolved, as that step is performed after yielding. This is done so we can have our own logic - // to log warnings or throw errors from here while we're processing interfaces in this module. - if (discoveryState.TrackFailedResolutionType(interfaceSignature, module)) - { - WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - discoveryState.TrackGenericInterfaceType(interfaceSignature, interopReferences); - } - - continue; - } + // Track the constructed generic type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackGenericTypeInstance( + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } catch (Exception e) @@ -464,33 +254,13 @@ private static void DiscoverSzArrayTypes( { args.Token.ThrowIfCancellationRequested(); - // Filter all constructed generic type signatures we have. We don't care about - // generic type definitions (eg. '!0[]') for the purposes of marshalling code. - if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) - { - continue; - } - - // Ignore types that are not fully resolvable (this likely means a .dll is missing) - if (!typeSignature.IsFullyResolvable(out _)) - { - // Log a warning the first time we fail to resolve this SZ array in this module - if (discoveryState.TrackFailedResolutionType(typeSignature, module)) - { - WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - // Ignore array types that are not Windows Runtime types - if (!typeSignature.IsWindowsRuntimeType(interopReferences)) - { - continue; - } - - // Track all SZ array types, as we'll need to emit marshalling code for them - discoveryState.TrackSzArrayType(typeSignature); + // Track the SZ array type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackSzArrayType( + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } catch (Exception e) diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 348103964..ae5fd3a27 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -2252,7 +2252,8 @@ public MemberReference IAsyncOperation1GetResults(TypeSignature resultType) return IAsyncOperation1 .MakeGenericReferenceType(resultType) .ToTypeDefOrRef() - .CreateMemberReference("GetResults"u8, MethodSignature.CreateInstance(resultType)); + .CreateMemberReference("GetResults"u8, MethodSignature.CreateInstance( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0))); } /// @@ -2375,7 +2376,8 @@ public MemberReference IAsyncOperationWithProgress2GetResults(TypeSignature resu return IAsyncOperationWithProgress2 .MakeGenericReferenceType(resultType, progressType) .ToTypeDefOrRef() - .CreateMemberReference("GetResults"u8, MethodSignature.CreateInstance(new GenericParameterSignature(GenericParameterType.Type, 0))); + .CreateMemberReference("GetResults"u8, MethodSignature.CreateInstance( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0))); } ///