Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
14977e3
Add IID for IMapChangedEventArgs1 interface
Sergio0694 Dec 18, 2025
43fa9c3
Refactor delegate invoke method body construction
Sergio0694 Dec 18, 2025
9143aac
Refactor delegate exception handling labels
Sergio0694 Dec 18, 2025
419a6ad
Inline ldloca_0_invoke instruction in delegate builder
Sergio0694 Dec 18, 2025
e4fde7e
Add NativeParameter marshalling logic to Interop factory
Sergio0694 Dec 18, 2025
eb9d32b
Handle Exception type marshalling in native parameter rewrite
Sergio0694 Dec 19, 2025
a73424c
Add IsTypeOfVoidPointer method to WindowsRuntimeExtensions
Sergio0694 Dec 19, 2025
58f4a18
Implement marshalling logic for native parameters
Sergio0694 Dec 19, 2025
f62370a
Add NativeParameter class for method rewrite info
Sergio0694 Dec 19, 2025
d7ae2e9
Add support for native parameter method rewrite
Sergio0694 Dec 19, 2025
872290d
Add tracking for native parameter method rewrites
Sergio0694 Dec 19, 2025
ce92a0a
Add emitState tracking to NativeDelegateType method
Sergio0694 Dec 19, 2025
250a2b9
Add IListExtensions with ReferenceIndexOf method
Sergio0694 Dec 19, 2025
311343d
Add ReferenceContains method to IListExtensions
Sergio0694 Dec 19, 2025
c766bc1
Use reference-based collection methods for instruction checks
Sergio0694 Dec 19, 2025
f47b27a
Add ReferenceRemove and ReferenceRemoveRange extensions
Sergio0694 Dec 19, 2025
dc5f9fc
Rename ReplaceRange to ReferenceReplaceRange in instruction helpers
Sergio0694 Dec 19, 2025
7ef6023
Refactor MethodRewriteInfo comparison logic
Sergio0694 Dec 19, 2025
4840649
Refactor vtable sharing checks to use ABI type void*
Sergio0694 Dec 19, 2025
cd48e28
Add HasReferenceAbiType method to WindowsRuntimeExtensions
Sergio0694 Dec 19, 2025
acee4a4
Refactor ABI type checks to use HasReferenceAbiType
Sergio0694 Dec 19, 2025
746c9a0
Refactor delegate vtable creation for custom types
Sergio0694 Dec 19, 2025
1a0a7a4
Rename sharedReadOnlyDictionaryType to sharedDictionaryType
Sergio0694 Dec 19, 2025
d667fea
Add shared vtable type support for delegate interop
Sergio0694 Dec 19, 2025
fbb1ce9
Pass vftblType to Delegate.ImplType method
Sergio0694 Dec 19, 2025
401c4dd
Clarify XML doc references to AsmResolver.DotNet.TypeReference
Sergio0694 Dec 20, 2025
e67f2e5
Handle Type parameters in interop method rewriting
Sergio0694 Dec 20, 2025
84378f6
Add references for Nullable<int> and HString marshalling
Sergio0694 Dec 20, 2025
6f40afa
Handle string parameters in interop method rewriting
Sergio0694 Dec 20, 2025
d65e01c
Fix incorrect value type for dictionaries
Sergio0694 Dec 20, 2025
a3af887
Update XML docs for ABI type methods
Sergio0694 Dec 20, 2025
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public static void Vftbl(
TypeSignature keyType = dictionaryType.TypeArguments[0];
TypeSignature valueType = dictionaryType.TypeArguments[1];

bool isKeyReferenceType = !keyType.IsValueType || keyType.IsConstructedKeyValuePairType(interopReferences);
bool isValueReferenceType = !valueType.IsValueType || valueType.IsConstructedKeyValuePairType(interopReferences);
bool isKeyReferenceType = keyType.HasReferenceAbiType(interopReferences);
bool isValueReferenceType = valueType.HasReferenceAbiType(interopReferences);

// We can share the vtable type for 'void*' when both key and value types are reference types
if (isKeyReferenceType && isValueReferenceType)
Expand Down Expand Up @@ -135,14 +135,14 @@ static void GetOrCreateVftbl(
}

// Create a dummy signature just to generate the mangled name for the vtable type
TypeSignature sharedReadOnlyDictionaryType = interopReferences.IDictionary2.MakeGenericReferenceType(
TypeSignature sharedDictionaryType = interopReferences.IDictionary2.MakeGenericReferenceType(
displayKeyType,
displayValueType);

// Construct a new specialized vtable type
TypeDefinition newVftblType = WellKnownTypeDefinitionFactory.IDictionary2Vftbl(
ns: InteropUtf8NameFactory.TypeNamespace(sharedReadOnlyDictionaryType),
name: InteropUtf8NameFactory.TypeName(sharedReadOnlyDictionaryType, "Vftbl"),
ns: InteropUtf8NameFactory.TypeNamespace(sharedDictionaryType),
name: InteropUtf8NameFactory.TypeName(sharedDictionaryType, "Vftbl"),
keyType: keyType,
valueType: valueType,
interopReferences: interopReferences,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,8 @@ public static void Vftbl(
{
TypeSignature elementType = listType.TypeArguments[0];

// All reference types can share the same vtable type (as it just uses 'void*' for the ABI type).
// We can also share vtables for 'KeyValuePair<,>' types, as their ABI type is an interface.
if (!elementType.IsValueType || elementType.IsConstructedKeyValuePairType(interopReferences))
// For types which use 'void*' as their ABI types, we can share the same vtable type definition
if (elementType.HasReferenceAbiType(interopReferences))
{
vftblType = interopDefinitions.IList1Vftbl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static void Vftbl(
// All reference types can share the same vtable type (as it just uses 'void*' for the ABI type).
// The 'IMapView<K, V>' interface doesn't use 'V' as a by-value parameter anywhere in the vtable,
// so we can aggressively share vtable types for all cases where 'K' is a reference type.
if (!keyType.IsValueType || keyType.IsConstructedKeyValuePairType(interopReferences))
if (keyType.HasReferenceAbiType(interopReferences))
{
vftblType = interopDefinitions.IReadOnlyDictionary2Vftbl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static void Vftbl(
TypeSignature elementType = readOnlyListType.TypeArguments[0];

// Same logic as with 'IList1.Vftbl' (i.e. share for all reference types)
if (!elementType.IsValueType || elementType.IsConstructedKeyValuePairType(interopReferences))
if (elementType.HasReferenceAbiType(interopReferences))
{
vftblType = interopDefinitions.IReadOnlyList1Vftbl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,34 @@ internal static class CilInstructionCollectionExtensions
extension(CilInstructionCollection instructions)
{
/// <summary>
/// Replaces a target instruction with a collection of new instructions.
/// Removes a set of CIL instructions from the collection.
/// </summary>
/// <param name="target">The instruction to replace.</param>
/// <param name="values">The new instructions to emit.</param>
public void ReplaceRange(CilInstruction target, params IEnumerable<CilInstruction> values)
/// <param name="items">The instructions to remove.</param>
public void ReferenceRemoveRange(params IEnumerable<CilInstruction> items)
{
int index;

// Find the index of the target instruction in the collection.
// We can't use 'IndexOf', as we only want to match by reference.
for (index = 0; index < instructions.Count; index++)
foreach (CilInstruction item in items)
{
if (instructions[index] == target)
{
break;
}
_ = instructions.ReferenceRemove(item);
}
}

/// <summary>
/// Replaces a target instruction with a collection of new instructions.
/// </summary>
/// <param name="target">The instruction to replace.</param>
/// <param name="items">The new instructions to emit.</param>
public void ReferenceReplaceRange(CilInstruction target, params IEnumerable<CilInstruction> items)
{
int index = instructions.ReferenceIndexOf(target);

// Ensure we did find the target instruction
if (index >= instructions.Count)
if (index == -1)
{
throw new ArgumentException("The target instruction was not found in the collection.", nameof(target));
}

instructions.RemoveAt(index);
instructions.InsertRange(index, values);
instructions.InsertRange(index, items);
}
}
}
60 changes: 60 additions & 0 deletions src/WinRT.Interop.Generator/Extensions/IListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Generic;

namespace WindowsRuntime.InteropGenerator;

/// <summary>
/// Extensions for the <see cref="IList{T}"/> type.
/// </summary>
internal static class IListExtensions
{
extension<T>(IList<T> list)
where T : class
{
/// <inheritdoc cref="List{T}.Contains(T)"/>
/// <remarks>
/// This method only ever compares values by reference equality.
/// </remarks>
public bool ReferenceContains(T value)
{
return list.Count != 0 && list.ReferenceIndexOf(value) >= 0;
}

/// <inheritdoc cref="List{T}.Remove(T)"/>
/// <remarks>
/// This method only ever compares values by reference equality.
/// </remarks>
public bool ReferenceRemove(T value)
{
int index = list.ReferenceIndexOf(value);

if (index >= 0)
{
list.RemoveAt(index);

return true;
}

return false;
}

/// <inheritdoc cref="IList{T}.IndexOf(T)"/>
/// <remarks>
/// This method only ever compares values by reference equality.
/// </remarks>
public int ReferenceIndexOf(T value)
{
for (int i = 0; i < list.Count; i++)
{
if (ReferenceEquals(list[i], value))
{
return i;
}
}

return -1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ public bool IsTypeOfObject()
return type is CorLibTypeSignature { ElementType: ElementType.Object };
}

/// <summary>
/// Checks whether an <see cref="ITypeDescriptor"/> is a <see cref="void"/> pointer type.
/// </summary>
/// <returns>Whether the type is a <see cref="void"/> pointer type.</returns>
public bool IsTypeOfVoidPointer()
{
return type is PointerTypeSignature { BaseType: CorLibTypeSignature { ElementType: ElementType.Void } };
}

/// <summary>
/// Checks whether an <see cref="ITypeDescriptor"/> represents a fundamental Windows Runtime type.
/// </summary>
Expand Down Expand Up @@ -477,11 +486,46 @@ public bool IsTrackerSupportRequired(InteropReferences interopReferences)
return false;
}

/// <summary>
/// Gets whether a given type has an ABI type that is a reference type.
/// </summary>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <returns>Whether the input type has an ABI type that is a reference type.</returns>
public bool HasReferenceAbiType(InteropReferences interopReferences)
{
// All constructed generics will use 'void*' for the ABI type
if (type is GenericInstanceTypeSignature)
{
return true;
}

// All other value types will never have a reference type as the ABI type
if (type.IsValueType)
{
return false;
}

// 'Type' is a class, but is custom-mapped to the 'TypeName' struct type
if (SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Type))
{
return false;
}

// 'Exception' is also a class, but is custom-mapped to the 'HResult' struct type
if (SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Exception))
{
return false;
}

// For all other cases (e.g. interfaces, classes, delegates, etc.), the ABI type is always a pointer
return true;
}

/// <summary>
/// Gets the ABI type for a given type.
/// </summary>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <returns>The ABi type for the input type.</returns>
/// <returns>The ABI type for the input type.</returns>
public TypeSignature GetAbiType(InteropReferences interopReferences)
{
// All constructed generics will use 'void*' for the ABI type. This applies to both reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,12 @@ public static void RewriteMethod(
}

// If we didn't find the marker, it means the target method is either invalid
if (!body.Instructions.Contains(marker))
if (!body.Instructions.ReferenceContains(marker))
{
throw WellKnownInteropExceptions.MethodRewriteMarkerInstructionNotFoundError(marker, method);
}

// If we didn't find the marker, it means the target method is either invalid, or the
// supplied marker was incorrect (or the caller forgot to add it to the method body).
// Validate that the target parameter index is in range
if ((uint)parameterIndex >= method.Parameters.Count)
{
throw WellKnownInteropExceptions.MethodRewriteParameterIndexNotValidError(parameterIndex, method);
Expand All @@ -72,12 +71,12 @@ public static void RewriteMethod(
// If the return type is blittable, we can just load it directly it directly (simplest case)
if (parameterType.IsBlittable(interopReferences))
{
body.Instructions.ReplaceRange(marker, CilInstruction.CreateLdarg(parameterIndex));
body.Instructions.ReferenceReplaceRange(marker, CilInstruction.CreateLdarg(parameterIndex));
}
else if (parameterType.IsConstructedKeyValuePairType(interopReferences))
{
// If the type is some constructed 'KeyValuePair<,>' type, we use the generated marshaller
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]);
}
Expand All @@ -96,7 +95,7 @@ public static void RewriteMethod(
parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));

// Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable<T>' value
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, marshallerMethod.Import(module))]);
}
Expand All @@ -114,22 +113,22 @@ public static void RewriteMethod(
parameterTypes: [parameterType.GetAbiType(interopReferences)]));

// We can directly call the marshaller and return it, no 'try/finally' complexity is needed
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, marshallerMethod.Import(module))]);
}
}
else if (parameterType.IsTypeOfString())
{
// When marshalling 'string' values, we must use 'HStringMarshaller' (the ABI type is not actually a COM object)
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged.Import(module))]);
}
else if (parameterType is GenericInstanceTypeSignature)
{
// This case (constructed interfaces or delegates) is effectively identical to marshalling 'KeyValuePair<,>' values
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]);
}
Expand All @@ -146,7 +145,7 @@ public static void RewriteMethod(
parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));

// Marshal the value and release the original interface pointer
body.Instructions.ReplaceRange(marker, [
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, marshallerMethod.Import(module))]);
}
Expand Down
Loading
Loading