Skip to content

Commit

Permalink
First iteration of making CsWinRT compatible with IL trimming (#1224)
Browse files Browse the repository at this point in the history
* Initial attempt at IL trimming compat by using attribute lookup instead of type lookup for helper types.

* Remove commented out helper type for custom projected types.

* Make Helper type lookup trim friendly

* Mark Type FromABI trim unfriendly for now to see impact

* Make test projections trimmable

* Add more annotations to support trimming and handle trimmed types.

* Add functional tests

* Update testwinrt

* Remove attributes from runtime class name as it is not an qualified type name and attempt to address issue where Xaml metadata providers asks for trimmed types.

* Experiment with symbols

* Fix WinUI type lookup from base class being given as a type.

* Fix interfaces being trimmed affecting CCW / vtables on managed objects

* Propgate more attributes.

* Fix build

* Fix issue with eventhandler getting trimmed

* Enable trimming of WinUI projection

* Update attribute to reflect that we handle trimmed types.

* Add more tests

* Update testwinrt and add supress for MakeGenericType in nullable

* Run functional tests in pipeline

* Move publishing to build

* Fix tests

* Fix build break

* Fix build

* Fix build

* Revert file with no changes.

* Remove accidental commit

* PR feedback

* Fix base type lookup not being recursive

* Fix casing

* Try removing NET6_0
  • Loading branch information
manodasanW authored Jul 19, 2022
1 parent 1ad15ea commit d6fb9c4
Show file tree
Hide file tree
Showing 73 changed files with 1,653 additions and 178 deletions.
12 changes: 11 additions & 1 deletion build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ stages:
- job: BuildAndTest
pool:
vmImage: windows-latest
timeoutInMinutes: 90
timeoutInMinutes: 120
# https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
strategy:
maxParallel: 10
Expand Down Expand Up @@ -113,6 +113,16 @@ stages:
_build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:AUTHORINGTEST-$(Build.BuildNumber).xml
exit /b 0
# Run Functional Tests
- task: CmdLine@2
displayName: Run Functional Tests
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
inputs:
workingDirectory: $(Build.SourcesDirectory)\src
script: |
set cswinrt_label=functionaltest
build.cmd $(BuildPlatform) $(BuildConfiguration) $(VersionNumber) $(Build.BuildNumber) $(WinRT.Runtime.AssemblyVersion)
- job: Benchmarks
displayName: Run Benchmarks
condition: or(eq(variables['_RunBenchmarks'],'true'), eq(variables['Build.Reason'],'Schedule'))
Expand Down
6 changes: 6 additions & 0 deletions src/Perf/IIDOptimizer/SignatureGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ bool TryGetDefaultInterfaceTypeForRuntimeClassType(TypeReference runtimeClassTyp
return false;
}

if (runtimeClassAttribute.ConstructorArguments[0].Value is TypeReference typeReference)
{
defaultInterface = typeReference;
return true;
}

string defaultInterfacePropertyName = (string)runtimeClassAttribute.ConstructorArguments[0].Value;

var defaultInterfaceProperty = rcDef.Properties.FirstOrDefault(prop => prop.Name == defaultInterfacePropertyName);
Expand Down
2 changes: 2 additions & 0 deletions src/Projections/Reunion/Reunion.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<Platforms>x64;x86</Platforms>
<AssemblyName>Microsoft.WinUI</AssemblyName>
<AssemblyVersion>9.9.9.9</AssemblyVersion>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/Projections/Test/Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<Platforms>x64;x86</Platforms>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion src/Projections/Test/TestHost.ProbeByHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
namespace Windows.Foundation
{
[global::WinRT.WindowsRuntimeType]
[Guid("00000035-0000-0000-c000-000000000046")]
[Guid("00000035-0000-0000-c000-000000000046")]
[WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IActivationFactory))]
internal interface IActivationFactory
{
Object ActivateInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
namespace Windows.Foundation
{
[global::WinRT.WindowsRuntimeType]
[Guid("00000035-0000-0000-c000-000000000046")]
[Guid("00000035-0000-0000-c000-000000000046")]
[WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IActivationFactory))]
internal interface IActivationFactory
{
Object ActivateInstance();
Expand Down
2 changes: 2 additions & 0 deletions src/Projections/WinUI/WinUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<Platforms>x64;x86</Platforms>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/Projections/Windows/Windows.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<Platforms>x64;x86</Platforms>
<AssemblyName>Microsoft.Windows.SDK.NET</AssemblyName>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
18 changes: 18 additions & 0 deletions src/Tests/FunctionalTests/Async/Async.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Platforms>x86;x64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
<PublishProfileFullPath>$(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml</PublishProfileFullPath>
<SimulateCsWinRTNugetReference>true</SimulateCsWinRTNugetReference>
<CsWinRTEnabled>false</CsWinRTEnabled>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Projections\Test\Test.csproj" />
<ProjectReference Include="..\..\..\Projections\Windows\Windows.csproj" />
</ItemGroup>

</Project>
65 changes: 65 additions & 0 deletions src/Tests/FunctionalTests/Async/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Threading.Tasks;
using TestComponentCSharp;

var instance = new Class();

instance.IntProperty = 12;
var async_get_int = instance.GetIntAsync();
int async_int = 0;
async_get_int.Completed = (info, status) => async_int = info.GetResults();
async_get_int.GetResults();

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

instance.StringProperty = "foo";
var async_get_string = instance.GetStringAsync();
string async_string = "";
async_get_string.Completed = (info, status) => async_string = info.GetResults();
int async_progress;
async_get_string.Progress = (info, progress) => async_progress = progress;
async_get_string.GetResults();

if (async_string != "foo")
{
return 102;
}

var task = InvokeAddAsync(instance, 20, 10);
if (task.Wait(25))
{
return 103;
}

instance.CompleteAsync();
if (!task.Wait(1000))
{
return 104;
}

if (task.Status != TaskStatus.RanToCompletion || task.Result != 30)
{
return 105;
}

var ports = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(
Windows.Devices.SerialCommunication.SerialDevice.GetDeviceSelector(),
new string[] { "System.ItemNameDisplay" });
foreach (var port in ports)
{
object o = port.Properties["System.ItemNameDisplay"];
if (o is null)
{
return 106;
}
}

return 100;

static async Task<int> InvokeAddAsync(Class instance, int lhs, int rhs)
{
return await instance.AddAsync(lhs, rhs);
}
18 changes: 18 additions & 0 deletions src/Tests/FunctionalTests/CCW/CCW.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Platforms>x86;x64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
<PublishProfileFullPath>$(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml</PublishProfileFullPath>
<SimulateCsWinRTNugetReference>true</SimulateCsWinRTNugetReference>
<CsWinRTEnabled>false</CsWinRTEnabled>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Projections\Test\Test.csproj" />
<ProjectReference Include="..\..\..\Projections\Windows\Windows.csproj" />
</ItemGroup>

</Project>
80 changes: 80 additions & 0 deletions src/Tests/FunctionalTests/CCW/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using TestComponentCSharp;
using WinRT.Interop;
using WinRT;

var managedProperties = new ManagedProperties(42);
var instance = new Class();

// Ensure we can use the IProperties interface from the native side.
instance.CopyProperties(managedProperties);
if (managedProperties.ReadWriteProperty != instance.ReadWriteProperty)
{
return 101;
}

// Check for the default interfaces provided by WinRT.Runtime
Guid IID_IMarshal = new Guid("00000003-0000-0000-c000-000000000046");
IObjectReference ccw = MarshalInterface<IProperties1>.CreateMarshaler(managedProperties);
ccw.TryAs<IUnknownVftbl>(IID_IMarshal, out var marshalCCW);
if (marshalCCW == null)
{
return 102;
}

// Check for managed implemented interface to ensure not trimmed.
Guid IID_IUriHandler = new Guid("FF4B4334-2104-537D-812E-67E3856AC7A2");
ccw.TryAs<IUnknownVftbl>(IID_IUriHandler, out var uriHandlerCCW);
if (uriHandlerCCW == null)
{
return 103;
}

// Ensure that interfaces on the vtable / object don't get trimmed even if unused.
Guid IID_IWarning1 = new Guid("4DB3FA26-4BB1-50EA-8362-98F49651E516");
Guid IID_IWarningClassOverrides = new Guid("E5635CE4-D483-55AA-86D5-080DC07F0A09");

var managedWarningClass = new ManagedWarningClass();
ccw = MarshalInterface<IUriHandler>.CreateMarshaler(managedWarningClass);
ccw.TryAs<IUnknownVftbl>(IID_IWarning1, out var warningCCW);
if (warningCCW == null)
{
return 104;
}

ccw.TryAs<IUnknownVftbl>(IID_IWarningClassOverrides, out var warningOverrideCCW);
if (warningOverrideCCW == null)
{
return 105;
}

return 100;

sealed class ManagedProperties : IProperties1, IUriHandler
{
private readonly int _value;

public ManagedProperties(int value)
{
_value = value;
}

public int ReadWriteProperty => _value;

public void AddUriHandler(ProvideUri provideUri)
{
_ = provideUri();
}

void IUriHandler.AddUriHandler(ProvideUri provideUri) => AddUriHandler(provideUri);
}

sealed class ManagedWarningClass : WarningClass, IUriHandler
{
public void AddUriHandler(ProvideUri provideUri)
{
_ = provideUri();
}

void IUriHandler.AddUriHandler(ProvideUri provideUri) => AddUriHandler(provideUri);
}
18 changes: 18 additions & 0 deletions src/Tests/FunctionalTests/ClassActivation/ClassActivation.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Platforms>x86;x64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
<PublishProfileFullPath>$(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml</PublishProfileFullPath>
<SimulateCsWinRTNugetReference>true</SimulateCsWinRTNugetReference>
<CsWinRTEnabled>false</CsWinRTEnabled>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Projections\Test\Test.csproj" />
<ProjectReference Include="..\..\..\Projections\Windows\Windows.csproj" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions src/Tests/FunctionalTests/ClassActivation/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using TestComponentCSharp;

// new RCW / Factory activation
var instance = new Class();

var expectedEnum = EnumValue.Two;
instance.EnumProperty = expectedEnum;

// Custom type marshaling
var expectedUri = new Uri("http://expected");
instance.UriProperty = expectedUri;

var instance2 = new Class(32);

return instance.EnumProperty == expectedEnum &&
instance.UriProperty == expectedUri &&
instance2.IntProperty == 32 ? 100 : 101;
18 changes: 18 additions & 0 deletions src/Tests/FunctionalTests/Collections/Collections.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Platforms>x86;x64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
<PublishProfileFullPath>$(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml</PublishProfileFullPath>
<SimulateCsWinRTNugetReference>true</SimulateCsWinRTNugetReference>
<CsWinRTEnabled>false</CsWinRTEnabled>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Projections\Test\Test.csproj" />
<ProjectReference Include="..\..\..\Projections\Windows\Windows.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit d6fb9c4

Please sign in to comment.