Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 172 additions & 19 deletions src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,23 @@ public static void TryTrackGenericTypeInstance(
if (typeSignature.IsWindowsRuntimeType(interopReferences))
{
TryTrackWindowsRuntimeGenericTypeInstance(
typeDefinition,
typeSignature,
args,
discoveryState,
interopReferences,
module);
typeDefinition: typeDefinition,
typeSignature: typeSignature,
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);
}
else
{
// Otherwise, try to track information for some constructed managed type
TryTrackManagedGenericTypeInstance(
typeDefinition,
typeSignature,
args,
discoveryState,
interopReferences);
typeDefinition: typeDefinition,
typeSignature: typeSignature,
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);
}
}

Expand Down Expand Up @@ -165,7 +166,12 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance(
// Track all projected Windows Runtime generic interfaces
if (typeDefinition.IsInterface)
{
discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences);
TryTrackWindowsRuntimeGenericInterfaceTypeInstance(
typeSignature: typeSignature,
args: args,
discoveryState,
interopReferences: interopReferences,
module: module);

// We also want to crawl base interfaces
foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces())
Expand All @@ -191,11 +197,155 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance(
continue;
}

discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences);
TryTrackWindowsRuntimeGenericInterfaceTypeInstance(
typeSignature: constructedSignature,
args: args,
discoveryState,
interopReferences: interopReferences,
module: module);
}
}
}

/// <summary>
/// Tries to track a constructed generic Windows Runtime interface type.
/// </summary>
/// <param name="typeSignature">The <see cref="GenericInstanceTypeSignature"/> for the constructed type to analyze.</param>
/// <param name="args">The arguments for this invocation.</param>
/// <param name="discoveryState">The discovery state for this invocation.</param>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <param name="module">The module currently being analyzed.</param>
private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance(
GenericInstanceTypeSignature typeSignature,
InteropGeneratorArgs args,
InteropGeneratorDiscoveryState discoveryState,
InteropReferences interopReferences,
ModuleDefinition module)
{
if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerator1))
{
discoveryState.TrackIEnumerator1Type(typeSignature);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerable1))
{
discoveryState.TrackIEnumerable1Type(typeSignature);

// We need special handling for 'IEnumerator<T>' types whenever we discover any constructed 'IEnumerable<T>'
// type. This ensures that we're never missing any 'IEnumerator<T>' instantiation, which we might depend on
// from other generated code, or projections. This special handling is needed because unlike with the other
// interfaces, 'IEnumerator<T>' will not show up as a base interface for other collection interface types.
discoveryState.TrackIEnumerator1Type(interopReferences.IEnumerator1.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IList1))
{
discoveryState.TrackIList1Type(typeSignature);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyList1))
{
discoveryState.TrackIReadOnlyList1Type(typeSignature);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IDictionary2))
{
discoveryState.TrackIDictionary2Type(typeSignature);

// When discovering dictionary types, make sure to also track 'KeyValuePair<TKey, TValue>' types. Those will
// be needed when generating code for 'IEnumerator<KeyValuePair<TKey, TValue>>' types, which will be discovered
// automatically. However, the same is not true the constructed 'KeyValuePair<TKey, TValue>' types themselves.
// This is for the same reason why we need the other special cases in this method: members are not analyzed.
discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments]));

// When we discover a constructed 'IDictionary<TKey, TValue>' instantiation, we'll be generating a native object type during
// the emit phase, which is used to marshal anonymous objects. This derives from 'WindowsRuntimeDictionary<TKey, TValue, ...>'.
// For the 'Keys' and 'Values' properties, that base type will return instances of the 'DictionaryKeyCollection<TKey, TValue>'
// and 'DictionaryValueCollection<TKey, TValue>' types, respectively. Those instantiations will not be seen by 'cswinrtgen',
// because they will only exist in the final 'WinRT.Interop.dll' assembly being generated, and not in any input assemblies.
// So to ensure that we can still correctly marshal those to native, if needed, we manually track them as if we had seen them.
TryTrackGenericTypeInstance(
typeSignature: interopReferences.DictionaryKeyCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]),
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);

// Handle 'DictionaryValueCollection<TKey, TValue>' as well
TryTrackGenericTypeInstance(
typeSignature: interopReferences.DictionaryValueCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]),
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyDictionary2))
{
discoveryState.TrackIReadOnlyDictionary2Type(typeSignature);

// Same handling as above for constructed 'KeyValuePair<TKey, TValue>' types
discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments]));

// Handle 'ReadOnlyDictionaryKeyCollection<TKey, TValue>' as above
TryTrackGenericTypeInstance(
typeSignature: interopReferences.ReadOnlyDictionaryKeyCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]),
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);

// Handle 'ReadOnlyDictionaryValueCollection<TKey, TValue>' as well
TryTrackGenericTypeInstance(
typeSignature: interopReferences.ReadOnlyDictionaryValueCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]),
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableVector1))
{
discoveryState.TrackIObservableVector1Type(typeSignature);

// We need special handling for constructed 'VectorChangedEventHandler<T>' types, as those are required for each
// discovered 'IObservableVector<T>' type. These are not necessarily discovered in the same way, as while we are
// recursively constructing interfaces, we don't have the same logic for delegate types (or for types used in
// any signature of interface members). Because we only need this delegate type and the one below, we can just
// special case it. That is, we manually construct it every time we discover a constructed 'IObservableVector<T>'.
discoveryState.TrackGenericDelegateType(interopReferences.VectorChangedEventHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableMap2))
{
discoveryState.TrackIObservableMap2Type(typeSignature);

// Same handling as above for 'MapChangedEventHandler<K,V>' types
discoveryState.TrackGenericDelegateType(interopReferences.MapChangedEventHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IMapChangedEventArgs1))
{
discoveryState.TrackIMapChangedEventArgs1Type(typeSignature);
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncActionWithProgress1))
{
discoveryState.TrackIAsyncActionWithProgress1Type(typeSignature);

// Ensure that the delegate types for this instantiation of 'IAsyncActionWithProgress<TProgress>' are also tracked.
// Same rationale as above for the other special cased types. Same below as well for the other async info types.
discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionProgressHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionWithProgressCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperation1))
{
discoveryState.TrackIAsyncOperation1Type(typeSignature);

// Same handling as above for 'AsyncOperationCompletedHandler<TResult>'
discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperationWithProgress2))
{
discoveryState.TrackIAsyncOperationWithProgress2Type(typeSignature);

// Same handling as above for 'AsyncOperationProgressHandler<TResult, TProgress>' and 'AsyncOperationWithProgressCompletedHandler<TResult, TProgress>'
discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationProgressHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationWithProgressCompletedHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments]));
}
}

/// <summary>
/// Tries to track a constructed generic user-defined type.
/// </summary>
Expand All @@ -204,12 +354,14 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance(
/// <param name="args">The arguments for this invocation.</param>
/// <param name="discoveryState">The discovery state for this invocation.</param>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <param name="module">The module currently being analyzed.</param>
private static void TryTrackManagedGenericTypeInstance(
TypeDefinition typeDefinition,
GenericInstanceTypeSignature typeSignature,
InteropGeneratorArgs args,
InteropGeneratorDiscoveryState discoveryState,
InteropReferences interopReferences)
InteropReferences interopReferences,
ModuleDefinition module)
{
// Check for all '[ReadOnly]Span<T>' types in particular, and track them as SZ array types.
// This is because "pass-array" and "fill-array" parameters are projected using spans, but
Expand All @@ -227,10 +379,11 @@ private static void TryTrackManagedGenericTypeInstance(

// Otherwise, try to track a constructed user-defined type
TryTrackExposedUserDefinedType(
typeDefinition,
typeSignature,
args,
discoveryState,
interopReferences);
typeDefinition: typeDefinition,
typeSignature: typeSignature,
args: args,
discoveryState: discoveryState,
interopReferences: interopReferences,
module: module);
}
}
11 changes: 9 additions & 2 deletions src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static void TryTrackTypeHierarchyType(
/// <param name="args">The arguments for this invocation.</param>
/// <param name="discoveryState">The discovery state for this invocation.</param>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <param name="module">The module currently being analyzed.</param>
/// <remarks>
/// This method expects <paramref name="typeDefinition"/> to either be non-generic, or
/// to have <paramref name="typeSignature"/> be a fully constructed signature for it.
Expand All @@ -78,7 +79,8 @@ public static void TryTrackExposedUserDefinedType(
TypeSignature typeSignature,
InteropGeneratorArgs args,
InteropGeneratorDiscoveryState discoveryState,
InteropReferences interopReferences)
InteropReferences interopReferences,
ModuleDefinition module)
{
// Ignore types that should explicitly be excluded
if (TypeExclusions.IsExcluded(typeDefinition, interopReferences))
Expand Down Expand Up @@ -158,7 +160,12 @@ public static void TryTrackExposedUserDefinedType(
// So the discovery logic for generic instantiations below would otherwise miss it.
if (interfaceSignature is GenericInstanceTypeSignature constructedSignature)
{
discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences);
TryTrackWindowsRuntimeGenericInterfaceTypeInstance(
typeSignature: constructedSignature,
args: args,
discoveryState,
interopReferences: interopReferences,
module: module);
}
}
else if (interfaceDefinition.IsGeneratedComInterfaceType)
Expand Down
Loading