Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix GetEnumerator when C# enumerator gets passed to native and back #1735

Merged
merged 5 commits into from
Sep 7, 2024
Merged
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
2 changes: 1 addition & 1 deletion build/AzurePipelineTemplates/CsWinRT-Variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ variables:
- name: MinorVersion
value: 1
- name: PatchVersion
value: 1
value: 2
- name: WinRT.Runtime.AssemblyVersion
value: '2.1.0.0'
- name: Net5.SDK.Feed
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
<value>Class is not marked partial</value>
</data>
<data name="ClassNotMarkedPartial_Text" xml:space="preserve">
<value>Class '{0}' implements WinRT interfaces but isn't marked partial. Type should be marked partial for trimming and AOT compatibility if passed across the ABI.</value>
<value>Class '{0}' implements WinRT interfaces but isn't marked partial. Type should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI.</value>
</data>
<data name="DisjointNamespaceRule_Brief" xml:space="preserve">
<value>Namespace is disjoint from main (winmd) namespace</value>
Expand All @@ -130,7 +130,7 @@
<value>Project does not enable unsafe blocks</value>
</data>
<data name="EnableUnsafe_Text" xml:space="preserve">
<value>Type '{0}' implements generic WinRT interfaces which requires generated code using unsafe for trimming and AOT compatibility if passed across the ABI. Project needs to be updated with '&lt;AllowUnsafeBlocks&gt;true&lt;/AllowUnsafeBlocks&gt;'.</value>
<value>Type '{0}' implements generic WinRT interfaces which requires generated code using unsafe for trimming and AOT compatibility if passed across the WinRT ABI. Project needs to be updated with '&lt;AllowUnsafeBlocks&gt;true&lt;/AllowUnsafeBlocks&gt;'.</value>
</data>
<data name="GenericTypeRule_Brief" xml:space="preserve">
<value>Class (or interface) is generic</value>
Expand Down
2 changes: 1 addition & 1 deletion src/Tests/FunctionalTests/CCW/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ partial class Language
{
private readonly string[] _values = new string[4];

public string Name { get; } = "Language";
public string Name { get; init; } = "Language";
public int Value { get; set; }
public string this[int i]
{
Expand Down
59 changes: 59 additions & 0 deletions src/Tests/FunctionalTests/Collections/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,65 @@
return 101;
}

int sum = 0;
var enumerator = instance.GetIteratorForCollection(expected);
while (enumerator.MoveNext())
{
sum += enumerator.Current;
}

if (sum != 3)
{
return 101;
}

sum = 0;

CustomIterableTest customIterableTest = new CustomIterableTest();
foreach (var i in customIterableTest)
{
sum += i;
}

if (sum != 7)
{
return 101;
}

sum = 0;

var arr = new int[] { 2, 4, 6 };
CustomIterableTest customIterableTest2 = new CustomIterableTest(arr);
foreach (var i in customIterableTest2)
{
sum += i;
}

if (sum != 12)
{
return 101;
}

CustomIteratorTest iterator = new CustomIteratorTest();
iterator.MoveNext();
if (iterator.Current != 2)
{
return 101;
}

sum = 0;

var customIterableTest3 = CustomIterableTest.CreateWithCustomIterator();
foreach (var i in customIterableTest3)
{
sum += i;
}

if (sum != 7)
{
return 101;
}

return 100;

static bool SequencesEqual<T>(IEnumerable<T> x, params IEnumerable<T>[] list) => list.All((y) => x.SequenceEqual(y));
Expand Down
5 changes: 5 additions & 0 deletions src/Tests/TestComponentCSharp/Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,11 @@ namespace winrt::TestComponentCSharp::implementation
return winrt::single_threaded_vector(std::vector{ first, second });
}

IIterator<int32_t> Class::GetIteratorForCollection(IIterable<int32_t> iterable)
{
return iterable.First();
}

IBindableIterable Class::BindableIterableProperty()
{
return _bindableIterable;
Expand Down
2 changes: 2 additions & 0 deletions src/Tests/TestComponentCSharp/Class.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ namespace winrt::TestComponentCSharp::implementation
Windows::Foundation::Collections::IIterable<TestComponentCSharp::EnumValue> GetEnumIterable();
Windows::Foundation::Collections::IIterable<TestComponentCSharp::CustomDisposableTest> GetClassIterable();

Windows::Foundation::Collections::IIterator<int32_t> GetIteratorForCollection(Windows::Foundation::Collections::IIterable<int32_t> iterable);

Microsoft::UI::Xaml::Interop::IBindableIterable BindableIterableProperty();
void BindableIterableProperty(Microsoft::UI::Xaml::Interop::IBindableIterable const& value);
void RaiseBindableIterableChanged();
Expand Down
39 changes: 39 additions & 0 deletions src/Tests/TestComponentCSharp/CustomIterableTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "pch.h"
#include "CustomIterableTest.h"
#include "CustomIterableTest.g.cpp"

namespace winrt::TestComponentCSharp::implementation
{
CustomIterableTest::CustomIterableTest()
{
_iterable = winrt::single_threaded_vector(std::vector{ 1, 2, 4 });
}

CustomIterableTest::CustomIterableTest(winrt::Windows::Foundation::Collections::IIterable<int32_t> const& iterable)
{
_iterable = iterable;
}

CustomIterableTest::CustomIterableTest(bool useCustomIterator)
:CustomIterableTest()
{
_useCustomIterator = useCustomIterator;
}

winrt::TestComponentCSharp::CustomIterableTest CustomIterableTest::CreateWithCustomIterator()
{
return winrt::make<CustomIterableTest>(true);
}

winrt::Windows::Foundation::Collections::IIterator<int32_t> CustomIterableTest::First()
{
if (_useCustomIterator)
{
return winrt::TestComponentCSharp::CustomIteratorTest(_iterable.First());
}
else
{
return _iterable.First();
}
}
}
25 changes: 25 additions & 0 deletions src/Tests/TestComponentCSharp/CustomIterableTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once
#include "CustomIterableTest.g.h"

namespace winrt::TestComponentCSharp::implementation
{
struct CustomIterableTest : CustomIterableTestT<CustomIterableTest>
{
CustomIterableTest();
CustomIterableTest(winrt::Windows::Foundation::Collections::IIterable<int32_t> const& iterable);
CustomIterableTest(bool useCustomIterator);

static winrt::TestComponentCSharp::CustomIterableTest CreateWithCustomIterator();
winrt::Windows::Foundation::Collections::IIterator<int32_t> First();

winrt::Windows::Foundation::Collections::IIterable<int32_t> _iterable;
bool _useCustomIterator = false;
};
}

namespace winrt::TestComponentCSharp::factory_implementation
{
struct CustomIterableTest : CustomIterableTestT<CustomIterableTest, implementation::CustomIterableTest>
{
};
}
24 changes: 24 additions & 0 deletions src/Tests/TestComponentCSharp/ManualProjectionTestClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,44 @@ namespace winrt::TestComponentCSharp::implementation
{
}

CustomIteratorTest::CustomIteratorTest(winrt::Windows::Foundation::Collections::IIterator<int> iterator)
{
_iterator = iterator;
}
int32_t CustomIteratorTest::Current()
{
if (_iterator)
{
return _iterator.Current();
}

return 2;
}
bool CustomIteratorTest::HasCurrent()
{
if (_iterator)
{
return _iterator.HasCurrent();
}

return true;
}
bool CustomIteratorTest::MoveNext()
{
if (_iterator)
{
return _iterator.MoveNext();
}

return true;
}
uint32_t CustomIteratorTest::GetMany(array_view<int32_t> items)
{
if (_iterator)
{
return _iterator.GetMany(items);
}

throw hresult_not_implemented();
}
}
3 changes: 3 additions & 0 deletions src/Tests/TestComponentCSharp/ManualProjectionTestClasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ namespace winrt::TestComponentCSharp::implementation
struct CustomIteratorTest : CustomIteratorTestT<CustomIteratorTest>
{
CustomIteratorTest() = default;
CustomIteratorTest(winrt::Windows::Foundation::Collections::IIterator<int> iterator);

int32_t Current();
bool HasCurrent();
bool MoveNext();
uint32_t GetMany(array_view<int32_t> items);

winrt::Windows::Foundation::Collections::IIterator<int> _iterator;
};
}

Expand Down
12 changes: 12 additions & 0 deletions src/Tests/TestComponentCSharp/TestComponentCSharp.idl
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ namespace TestComponentCSharp
Windows.Foundation.Collections.IIterable<EnumValue> GetEnumIterable();
Windows.Foundation.Collections.IIterable<CustomDisposableTest> GetClassIterable();

Windows.Foundation.Collections.IIterator<Int32> GetIteratorForCollection(Windows.Foundation.Collections.IIterable<Int32> iterable);

// Bindable
Microsoft.UI.Xaml.Interop.IBindableIterable BindableIterableProperty;
void RaiseBindableIterableChanged();
Expand Down Expand Up @@ -516,6 +518,16 @@ namespace TestComponentCSharp
runtimeclass CustomIteratorTest : Windows.Foundation.Collections.IIterator<Int32>
{
CustomIteratorTest();
CustomIteratorTest(Windows.Foundation.Collections.IIterator<Int32> iterator);
}

[default_interface]
runtimeclass CustomIterableTest : Windows.Foundation.Collections.IIterable<Int32>
{
CustomIterableTest();
CustomIterableTest(Windows.Foundation.Collections.IIterable<Int32> iterable);

static CustomIterableTest CreateWithCustomIterator();
}

// SupportedOSPlatform warning tests
Expand Down
2 changes: 2 additions & 0 deletions src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<ClInclude Include="ClassWithExplicitIUnknown.h" />
<ClInclude Include="CustomExperimentClass.h" />
<ClInclude Include="CustomEquals.h" />
<ClInclude Include="CustomIterableTest.h" />
<ClInclude Include="ManualProjectionTestClasses.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Class.h">
Expand All @@ -105,6 +106,7 @@
<ClCompile Include="ClassWithExplicitIUnknown.cpp" />
<ClCompile Include="CustomExperimentClass.cpp" />
<ClCompile Include="CustomEquals.cpp" />
<ClCompile Include="CustomIterableTest.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
<ClCompile Include="CustomExperimentClass.cpp" />
<ClCompile Include="WinRT.Class.cpp" />
<ClCompile Include="ClassWithExplicitIUnknown.cpp" />
<ClCompile Include="NonUniqueClass.cpp" />
<ClCompile Include="CustomIterableTest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand All @@ -39,6 +41,8 @@
<ClInclude Include="CustomExperimentClass.h" />
<ClInclude Include="WinRT.Class.h" />
<ClInclude Include="ClassWithExplicitIUnknown.h" />
<ClInclude Include="NonUniqueClass.h" />
<ClInclude Include="CustomIterableTest.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="TestComponentCSharp.idl" />
Expand Down
14 changes: 13 additions & 1 deletion src/WinRT.Runtime/Projections/IEnumerable.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ static IEnumerableMethods()
{
return new global::ABI.System.Collections.Generic.FromAbiEnumerator<T>(ienumeratorImpl);
}
else if (first is global::ABI.System.Collections.Generic.ToAbiEnumeratorAdapter<T> toAbiAdapter)
{
// Unwrap to get the C# enumerator if it was originally one.
return toAbiAdapter.m_enumerator;
}
else if (first is IWinRTObject winrtObject)
{
// This is a projected RCW implementing IEnumerator<T>, but since it is being used
// as part of GetEnumerator, we need to remap the enumerator starting index.
// Due to that, we don't return the RCW itself.
return new global::ABI.System.Collections.Generic.FromAbiEnumerator<T>(winrtObject.NativeObject);
}

throw new InvalidOperationException("Unexpected type for enumerator");
}
Expand Down Expand Up @@ -939,7 +951,7 @@ public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces()
// we can't make them AOT friendly due to we can't reference them.
public sealed class ToAbiEnumeratorAdapter<T> : global::System.Collections.Generic.IEnumerator<T>, global::System.Collections.IEnumerator
{
private readonly global::System.Collections.Generic.IEnumerator<T> m_enumerator;
internal readonly global::System.Collections.Generic.IEnumerator<T> m_enumerator;

internal ToAbiEnumeratorAdapter(global::System.Collections.Generic.IEnumerator<T> enumerator) => m_enumerator = enumerator;

Expand Down