Skip to content
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
6 changes: 3 additions & 3 deletions build/AzurePipelineTemplates/CsWinRT-Variables.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
variables:
MajorVersion: 2
MinorVersion: 0
PatchVersion: 8
WinRT.Runtime.AssemblyVersion: '2.0.8.0'
MinorVersion: 1
PatchVersion: 0
WinRT.Runtime.AssemblyVersion: '2.1.0.0'
Net5.SDK.Feed: 'https://dotnetcli.blob.core.windows.net/dotnet'
Net6.SDK.Version: '6.0.420'
Net8.SDK.Version: '8.0.103'
Expand Down
16 changes: 16 additions & 0 deletions nuget/Microsoft.Windows.CsWinRT.targets
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<CompilerVisibleProperty Include="CsWinRTAotExportsEnabled" />
<CompilerVisibleProperty Include="CsWinRTRcwFactoryFallbackGeneratorForceOptIn" />
<CompilerVisibleProperty Include="CsWinRTRcwFactoryFallbackGeneratorForceOptOut" />
<CompilerVisibleProperty Include="CsWinRTCcwLookupTableGeneratorEnabled" />
</ItemGroup>

<Import Project="$(MSBuildThisFileDirectory)Microsoft.Windows.CsWinRT.Embedded.targets" Condition="'$(CsWinRTEmbedded)' == 'true'"/>
Expand All @@ -56,6 +57,21 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<Error Text="Support for .NET 5 ended with C#/WinRT 2.0. For .NET 5 support, use C#/WinRT version 1.6.5. See https://github.com/microsoft/CsWinRT/discussions/1232" />
</Target>

<!-- Note this runs before the msbuild editor config file is generated because that is what is used to pass properties to the source generator. -->
<Target Name="CsWinRTSetGeneratorProperties" BeforeTargets="GenerateMSBuildEditorConfigFile;GenerateMSBuildEditorConfigFileCore">
<PropertyGroup>
<CsWinRTCcwLookupTableGeneratorEnabled Condition="'$(CsWinRTCcwLookupTableGeneratorEnabled)' == '' and
'$(CsWinRTComponent)' == 'true'">true</CsWinRTCcwLookupTableGeneratorEnabled>
<!-- If the lookup generator is enabled, the AOT source generator generates vtable entries for generic types and boxing scenarios that it detects. -->
<!-- This is not intended to be ran on projections, so we try to detect that and disable it if a projection is being generated. -->
<CsWinRTCcwLookupTableGeneratorEnabled Condition="'$(CsWinRTCcwLookupTableGeneratorEnabled)' == '' and
'$(CsWinRTGenerateProjection)' == 'true' and
'$(OutputType)' == 'Library' and
('@(CsWinRTIncludes)' != ''or $(CsWinRTFilters.Contains('-include')))">false</CsWinRTCcwLookupTableGeneratorEnabled>
<CsWinRTCcwLookupTableGeneratorEnabled Condition="'$(CsWinRTCcwLookupTableGeneratorEnabled)' == ''">true</CsWinRTCcwLookupTableGeneratorEnabled>
</PropertyGroup>
</Target>

<!-- Remove WinRT.Host.dll and WinRT.Host.Shim.dll references -->
<Target Name="CsWinRTRemoveHostingDllReferences" AfterTargets="ResolvePackageAssets" BeforeTargets="ResolveLockFileAnalyzers" Outputs="@(Reference)">
<PropertyGroup>
Expand Down
296 changes: 242 additions & 54 deletions src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/Authoring/WinRT.SourceGenerator/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ public static bool GetCsWinRTRcwFactoryFallbackGeneratorForceOptOut(this Analyze
return false;
}

public static bool IsCsWinRTCcwLookupTableGeneratorEnabled(this AnalyzerConfigOptionsProvider provider)
{
if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTCcwLookupTableGeneratorEnabled", out var csWinRTCcwLookupTableGeneratorEnabled))
{
return bool.TryParse(csWinRTCcwLookupTableGeneratorEnabled, out var isCsWinRTCcwLookupTableGeneratorEnabled) && isCsWinRTCcwLookupTableGeneratorEnabled;
}

return false;
}

public static bool ShouldGenerateWinMDOnly(this GeneratorExecutionContext context)
{
if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGenerateWinMDOnly", out var CsWinRTGenerateWinMDOnlyStr))
Expand Down
2 changes: 1 addition & 1 deletion src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2725,7 +2725,7 @@ typeDeclaration.Node is INamedTypeSymbol symbol &&

if (vtableAttributesToAddOnLookupTable.Any())
{
WinRTAotSourceGenerator.GenerateVtableLookupTable(context.AddSource, (vtableAttributesToAddOnLookupTable.ToImmutableArray(), (true, true)), true);
WinRTAotSourceGenerator.GenerateVtableLookupTable(context.AddSource, (vtableAttributesToAddOnLookupTable.ToImmutableArray(), (true, true, true)), true);
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/Tests/AuthoringConsumptionTest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ TEST(AuthoringTest, CustomTypes)
EXPECT_EQ(pv, nullptr);
}

// Array marshaling on AOT needs dynamic code.
#ifndef AOT
auto erasedProjectedArrays = testClass.GetTypeErasedProjectedArrays();
EXPECT_EQ(erasedProjectedArrays.Size(), 7);
for (auto obj : erasedProjectedArrays)
Expand All @@ -355,6 +357,22 @@ TEST(AuthoringTest, CustomTypes)
EXPECT_NE(ra, nullptr);
auto type = ra.Type();
}
#endif
}

TEST(AuthoringTest, Async)
{
TestClass testClass;
auto asyncOperation = testClass.GetDoubleAsyncOperation();
EXPECT_EQ(asyncOperation.wait_for(std::chrono::seconds(2)), AsyncStatus::Completed);
EXPECT_EQ(asyncOperation.GetResults(), 4.0);

auto asyncOperation2 = testClass.GetStructAsyncOperation();
EXPECT_EQ(asyncOperation2.wait_for(std::chrono::seconds(2)), AsyncStatus::Completed);
auto result = asyncOperation2.GetResults();
EXPECT_EQ(result.X, 2);
EXPECT_EQ(result.Y, 4);
EXPECT_EQ(result.Value, L"Test");
}

TEST(AuthoringTest, CustomDictionaryImplementations)
Expand Down
15 changes: 15 additions & 0 deletions src/Tests/AuthoringTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
Expand Down Expand Up @@ -340,6 +341,20 @@ public IAsyncOperation<Int32> GetIntAsyncOperation()
return task.AsAsyncOperation();
}

public IAsyncOperationWithProgress<double, double> GetDoubleAsyncOperation()
{
return AsyncInfo.Run<double, double>(async (cancellationToken, progress) =>
{
await Task.Delay(100);
return 4.0;
});
}

public IAsyncOperation<BasicStruct> GetStructAsyncOperation()
{
return System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromResult(new BasicStruct() { X = 2, Y = 4, Value = "Test" });
}

public int SetIntAsyncOperation(IAsyncOperation<Int32> op)
{
return op.GetResults();
Expand Down
3 changes: 2 additions & 1 deletion src/WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ internal static List<ComInterfaceEntry> GetInterfaceTableEntries(Type type)
// which means the attribute lives on the authoring metadata type.
if (winrtExposedClassAttribute == null)
{
var authoringMetadaType = type.GetRuntimeClassCCWType();
// Using GetCCWType rather than GetRuntimeClassCCWType given we want to handle boxed value types.
var authoringMetadaType = type.GetCCWType();
if (authoringMetadaType != null)
{
winrtExposedClassAttribute = authoringMetadaType.GetCustomAttribute<WinRTExposedTypeAttribute>(false);
Expand Down
63 changes: 16 additions & 47 deletions src/WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using WinRT.Interop;
using static System.Runtime.InteropServices.ComWrappers;

Expand Down Expand Up @@ -301,80 +300,50 @@ static Type GetGenericImplType(Type implementationType)
}
}

private static readonly ReaderWriterLockSlim ComInterfaceEntriesLookupRwLock = new();
private static readonly List<Func<Type, ComInterfaceEntry[]>> ComInterfaceEntriesLookup = new();

public static void RegisterTypeComInterfaceEntriesLookup(Func<Type, ComInterfaceEntry[]> comInterfaceEntriesLookup)
{
ComInterfaceEntriesLookupRwLock.EnterWriteLock();
try
{
ComInterfaceEntriesLookup.Add(comInterfaceEntriesLookup);
}
finally
{
ComInterfaceEntriesLookupRwLock.ExitWriteLock();
}
ComInterfaceEntriesLookup.Add(comInterfaceEntriesLookup);
}

internal static ComInterfaceEntry[] GetComInterfaceEntriesForTypeFromLookupTable(Type type)
{
ComInterfaceEntriesLookupRwLock.EnterReadLock();

try
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = ComInterfaceEntriesLookup.Count;
for (int i = 0; i < count; i++)
{
foreach (var func in ComInterfaceEntriesLookup)
var comInterfaceEntries = ComInterfaceEntriesLookup[i](type);
if (comInterfaceEntries != null)
{
var comInterfaceEntries = func(type);
if (comInterfaceEntries != null)
{
return comInterfaceEntries;
}
return comInterfaceEntries;
}
}
finally
{
ComInterfaceEntriesLookupRwLock.ExitReadLock();
}

return null;
}

private static readonly ReaderWriterLockSlim RuntimeClassNameForNonWinRTTypeLookupRcwLock = new();
private static readonly List<Func<Type, string>> RuntimeClassNameForNonWinRTTypeLookup = new();

public static void RegisterTypeRuntimeClassNameLookup(Func<Type, string> runtimeClassNameLookup)
{
RuntimeClassNameForNonWinRTTypeLookupRcwLock.EnterWriteLock();
try
{
RuntimeClassNameForNonWinRTTypeLookup.Add(runtimeClassNameLookup);
}
finally
{
RuntimeClassNameForNonWinRTTypeLookupRcwLock.ExitWriteLock();
}
RuntimeClassNameForNonWinRTTypeLookup.Add(runtimeClassNameLookup);
}

internal static string GetRuntimeClassNameForNonWinRTTypeFromLookupTable(Type type)
{
RuntimeClassNameForNonWinRTTypeLookupRcwLock.EnterReadLock();

try
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = RuntimeClassNameForNonWinRTTypeLookup.Count;
for (int i = 0; i < count; i++)
{
foreach (var func in RuntimeClassNameForNonWinRTTypeLookup)
var runtimeClasName = RuntimeClassNameForNonWinRTTypeLookup[i](type);
if (!string.IsNullOrEmpty(runtimeClasName))
{
var runtimeClasName = func(type);
if (!string.IsNullOrEmpty(runtimeClasName))
{
return runtimeClasName;
}
return runtimeClasName;
}
}
finally
{
RuntimeClassNameForNonWinRTTypeLookupRcwLock.ExitReadLock();
}

return null;
}
Expand Down
15 changes: 11 additions & 4 deletions src/WinRT.Runtime/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ public static Type GetRuntimeClassCCWType(this Type type)
return type.IsClass && !type.IsArray ? type.GetAuthoringMetadataType() : null;
}

internal static Type GetCCWType(this Type type)
{
return !type.IsArray ? type.GetAuthoringMetadataType() : null;
}

private readonly static ConcurrentDictionary<Type, Type> AuthoringMetadataTypeCache = new();
private readonly static List<Func<Type, Type>> AuthoringMetadaTypeLookup = new();

Expand All @@ -300,12 +305,14 @@ internal static void RegisterAuthoringMetadataTypeLookup(Func<Type, Type> author
internal static Type GetAuthoringMetadataType(this Type type)
{
return AuthoringMetadataTypeCache.GetOrAdd(type,
(type) =>
static (type) =>
{
foreach (Func<Type, Type> func in AuthoringMetadaTypeLookup)
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = AuthoringMetadaTypeLookup.Count;
for (int i = 0; i < count; i++)
{
Type metadataType = func(type);

Type metadataType = AuthoringMetadaTypeLookup[i](type);
if (metadataType is not null)
{
return metadataType;
Expand Down
27 changes: 20 additions & 7 deletions src/WinRT.Runtime/TypeNameSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,20 @@ public static Type FindRcwTypeByNameCached(string runtimeClassName)
if (rcwType is null)
{
rcwType = baseRcwTypeCache.GetOrAdd(runtimeClassName,
(runtimeClassName) =>
{
var resolvedBaseType = projectionTypeNameToBaseTypeNameMappings.Find((dict) => dict.ContainsKey(runtimeClassName))?[runtimeClassName];
return resolvedBaseType is not null ? FindRcwTypeByNameCached(resolvedBaseType) : null;
static (runtimeClassName) =>
{
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = projectionTypeNameToBaseTypeNameMappings.Count;
for (int i = 0; i < count; i++)
{
if (projectionTypeNameToBaseTypeNameMappings[i].ContainsKey(runtimeClassName))
{
return FindRcwTypeByNameCached(projectionTypeNameToBaseTypeNameMappings[i][runtimeClassName]);
}
}

return null;
});
}

Expand All @@ -71,7 +81,7 @@ public static Type FindRcwTypeByNameCached(string runtimeClassName)
public static Type FindTypeByNameCached(string runtimeClassName)
{
return typeNameCache.GetOrAdd(runtimeClassName,
(runtimeClassName) =>
static (runtimeClassName) =>
{
Type implementationType = null;
try
Expand Down Expand Up @@ -184,9 +194,12 @@ private static Type FindTypeByNameCore(string runtimeClassName, Type[] genericTy
}
}

foreach (var assembly in projectionAssemblies)
// Using for loop to avoid exception from list changing when using for each.
// List is only added to and if any are added while looping, we can ignore those.
int count = projectionAssemblies.Count;
for (int i = 0; i < count; i++)
{
Type type = assembly.GetType(runtimeClassName);
Type type = projectionAssemblies[i].GetType(runtimeClassName);
if (type is not null)
{
resolvedType = type;
Expand Down