|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ILLink.Shared;
using Mono.Cecil;
namespace Mono.Linker
{
public class MemberActionStore
{
public SubstitutionInfo PrimarySubstitutionInfo { get; }
private readonly Dictionary<AssemblyDefinition, SubstitutionInfo?> _embeddedXmlInfos;
private readonly Dictionary<MethodDefinition, bool> _featureCheckValues;
readonly LinkContext _context;
public MemberActionStore (LinkContext context)
{
PrimarySubstitutionInfo = new SubstitutionInfo ();
_embeddedXmlInfos = new Dictionary<AssemblyDefinition, SubstitutionInfo?> ();
_featureCheckValues = new Dictionary<MethodDefinition, bool> ();
_context = context;
}
private bool TryGetSubstitutionInfo (MemberReference member, [NotNullWhen (true)] out SubstitutionInfo? xmlInfo)
{
var assembly = member.Module.Assembly;
if (!_embeddedXmlInfos.TryGetValue (assembly, out xmlInfo)) {
xmlInfo = _context.EmbeddedXmlInfo.ProcessSubstitutions (assembly, _context);
_embeddedXmlInfos.Add (assembly, xmlInfo);
}
return xmlInfo != null;
}
public MethodAction GetAction (MethodDefinition method)
{
if (PrimarySubstitutionInfo.MethodActions.TryGetValue (method, out MethodAction action))
return action;
if (TryGetSubstitutionInfo (method, out var embeddedXml)) {
if (embeddedXml.MethodActions.TryGetValue (method, out action))
return action;
}
if (TryGetFeatureCheckValue (method, out _))
return MethodAction.ConvertToStub;
return MethodAction.Nothing;
}
public bool TryGetMethodStubValue (MethodDefinition method, out object? value)
{
if (PrimarySubstitutionInfo.MethodStubValues.TryGetValue (method, out value))
return true;
if (TryGetSubstitutionInfo (method, out var embeddedXml)
&& embeddedXml.MethodStubValues.TryGetValue (method, out value))
return true;
if (TryGetFeatureCheckValue (method, out bool bValue)) {
value = bValue ? 1 : 0;
return true;
}
return false;
}
internal bool TryGetFeatureCheckValue (MethodDefinition method, out bool value)
{
if (_featureCheckValues.TryGetValue (method, out value))
return true;
value = false;
if (!method.IsStatic)
return false;
if (method.ReturnType.MetadataType != MetadataType.Boolean)
return false;
if (FindProperty (method) is not PropertyDefinition property)
return false;
if (property.SetMethod != null)
return false;
foreach (var featureSwitchDefinitionAttribute in _context.CustomAttributes.GetCustomAttributes (property, "System.Diagnostics.CodeAnalysis", "FeatureSwitchDefinitionAttribute")) {
if (featureSwitchDefinitionAttribute.ConstructorArguments is not [CustomAttributeArgument { Value: string switchName }])
continue;
// If there's a FeatureSwitchDefinition, don't continue looking for FeatureGuard.
// We don't want to infer feature switch settings from FeatureGuard.
if (_context.FeatureSettings.TryGetValue (switchName, out value)) {
_featureCheckValues[method] = value;
return true;
}
return false;
}
if (!_context.IsOptimizationEnabled (CodeOptimizations.SubstituteFeatureGuards, method))
return false;
foreach (var featureGuardAttribute in _context.CustomAttributes.GetCustomAttributes (property, "System.Diagnostics.CodeAnalysis", "FeatureGuardAttribute")) {
if (featureGuardAttribute.ConstructorArguments is not [CustomAttributeArgument { Value: TypeReference featureType }])
continue;
if (featureType.Namespace == "System.Diagnostics.CodeAnalysis") {
switch (featureType.Name) {
case "RequiresUnreferencedCodeAttribute":
_featureCheckValues[method] = value;
return true;
case "RequiresDynamicCodeAttribute":
if (_context.FeatureSettings.TryGetValue (
"System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported",
out bool isDynamicCodeSupported)
&& !isDynamicCodeSupported) {
_featureCheckValues[method] = value;
return true;
}
break;
}
}
}
return false;
static PropertyDefinition? FindProperty (MethodDefinition method) {
if (!method.IsGetter)
return null;
foreach (var property in method.DeclaringType.Properties) {
if (property.GetMethod == method)
return property;
}
return null;
}
}
public bool TryGetFieldUserValue (FieldDefinition field, out object? value)
{
if (PrimarySubstitutionInfo.FieldValues.TryGetValue (field, out value))
return true;
if (!TryGetSubstitutionInfo (field, out var embeddedXml))
return false;
return embeddedXml.FieldValues.TryGetValue (field, out value);
}
public bool HasSubstitutedInit (FieldDefinition field)
{
if (PrimarySubstitutionInfo.FieldInit.Contains (field))
return true;
if (!TryGetSubstitutionInfo (field, out var embeddedXml))
return false;
return embeddedXml.FieldInit.Contains (field);
}
}
}
|