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

Add support for feature switches, add option to disable dynamic objects support #1435

Merged
merged 4 commits into from
Jan 17, 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
17 changes: 17 additions & 0 deletions nuget/Microsoft.Windows.CsWinRT.targets
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,21 @@ $(CsWinRTInternalProjection)
<Import Project="$(MSBuildThisFileDirectory)Microsoft.Windows.CsWinRT.Authoring.targets" Condition="'$(CsWinRTComponent)' == 'true'"/>
<Import Project="$(MSBuildThisFileDirectory)Microsoft.Windows.CsWinRT.IIDOptimizer.targets" Condition="'$(CsWinRTIIDOptimizerOptOut)' != 'true'"/>

<!-- Default values for all custom CsWinRT runtimem feature switches -->
<PropertyGroup>
<CsWinRTEnableDynamicObjectsSupport Condition="'$(CsWinRTEnableDynamicObjectsSupport)' == ''">true</CsWinRTEnableDynamicObjectsSupport>
</PropertyGroup>

<!--
Configuration for the feature switches (to support IL trimming).
See the 'ILLink.Substitutions.xml' file for more details on that.
-->
<ItemGroup>

<!-- CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT switch -->
<RuntimeHostConfigurationOption Include="CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT"
Value="$(CsWinRTEnableDynamicObjectsSupport)"
Trim="true" />
</ItemGroup>

</Project>
80 changes: 80 additions & 0 deletions src/WinRT.Runtime/Configuration/FeatureSwitches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Runtime.CompilerServices;

namespace WinRT;

/// <summary>
/// A container for all shared <see cref="AppContext"/> configuration switches for CsWinRT.
/// </summary>
/// <remarks>
/// <para>
/// This type uses a very specific setup for configuration switches to ensure ILLink can work the best.
/// This mirrors the architecture of feature switches in the runtime as well, and it's needed so that
/// no static constructor is generated for the type.
/// </para>
/// <para>
/// For more info, see <see href="https://github.com/dotnet/runtime/blob/main/docs/workflow/trimming/feature-switches.md#adding-new-feature-switch"/>.
/// </para>
/// </remarks>
internal static class FeatureSwitches
{
/// <summary>
/// The configuration property name for <see cref="IsDebugOutputEnabled"/>.
/// </summary>
private const string IsDynamicObjectsSupportEnabledPropertyName = "CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT";

/// <summary>
/// The backing field for <see cref="IsDynamicObjectsSupportEnabled"/>.
/// </summary>
private static int _isDynamicObjectsSupportEnabled;

/// <summary>
/// Gets a value indicating whether or not projections support for dynamic objects is enabled (defaults to <see langword="true"/>).
/// </summary>
public static bool IsDynamicObjectsSupportEnabled
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetConfigurationValue(IsDynamicObjectsSupportEnabledPropertyName, ref _isDynamicObjectsSupportEnabled);
}

/// <summary>
/// Gets a configuration value for a specified property.
/// </summary>
/// <param name="propertyName">The property name to retrieve the value for.</param>
/// <param name="cachedResult">The cached result for the target configuration value.</param>
/// <returns>The value of the specified configuration setting.</returns>
private static bool GetConfigurationValue(string propertyName, ref int cachedResult)
{
// The cached switch value has 3 states:
// 0: unknown.
// 1: true
// -1: false
//
// This method doesn't need to worry about concurrent accesses to the cached result,
// as even if the configuration value is retrieved twice, that'll always be the same.
if (cachedResult < 0)
{
return false;
}

if (cachedResult > 0)
{
return true;
}

// Get the configuration switch value, or its default.
// All feature switches have a default set in the .targets file.
if (!AppContext.TryGetSwitch(propertyName, out bool isEnabled))
{
isEnabled = false;
}

// Update the cached result
cachedResult = isEnabled ? 1 : -1;

return isEnabled;
}
}
10 changes: 10 additions & 0 deletions src/WinRT.Runtime/Configuration/ILLink.Substitutions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<linker>
<assembly fullname="WinRT.Runtime">
<type fullname="WinRT.FeatureSwitches">

<!-- CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT switch -->
<method signature="System.Boolean get_IsDynamicObjectsSupportEnabled()" body="stub" value="false" feature="CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT" featurevalue="false"/>
<method signature="System.Boolean get_IsDynamicObjectsSupportEnabled()" body="stub" value="true" feature="CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT" featurevalue="true"/>
</type>
</assembly>
</linker>
10 changes: 9 additions & 1 deletion src/WinRT.Runtime/TypeNameSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,15 @@ public static (Type type, int remaining) FindTypeByName(ReadOnlySpan<char> runti
// It may be necessary to detect otherwise and return System.Object.
if (runtimeClassName.StartsWith("<>f__AnonymousType".AsSpan(), StringComparison.Ordinal))
{
return (typeof(System.Dynamic.ExpandoObject), 0);
if (FeatureSwitches.IsDynamicObjectsSupportEnabled)
{
return (typeof(System.Dynamic.ExpandoObject), 0);
}

throw new NotSupportedException(
$"""The requested runtime class name is "{runtimeClassName.ToString()}", which maps to a dynamic projected type. """ +
"""This can only be used when support for dynamic objects is enabled in the CsWinRT configuration. To enable it, """ +
"""make sure that the "CsWinRTEnableDynamicObjectsSupport" MSBuild property is not being set to 'false' anywhere.""");
}
// PropertySet and ValueSet can return IReference<String> but Nullable<String> is illegal
else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1<String>".AsSpan(), StringComparison.Ordinal) == 0)
Expand Down
5 changes: 5 additions & 0 deletions src/WinRT.Runtime/WinRT.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
<IsTrimmable>true</IsTrimmable>
<DefineConstants>$(DefineConstants);CsWinRT_LANG_11_FEATURES</DefineConstants>
</PropertyGroup>

<!-- Include the ILLink file (to properly trim configuration switches in publish builds) -->
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">
<EmbeddedResource Include="Configuration\ILLink.Substitutions.xml" LogicalName="ILLink.Substitutions.xml" />
</ItemGroup>

<ItemGroup>
<Compile Include="../cswinrt/strings/WinRT*.cs" Link="cswinrt/%(FileName)%(Extension)" />
Expand Down
Loading