|
// 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;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.XPath;
using ILLink.Shared;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public class BodySubstitutionParser : ProcessLinkerXmlBase
{
SubstitutionInfo? _substitutionInfo;
public BodySubstitutionParser(LinkContext context, Stream documentStream, string xmlDocumentLocation)
: base(context, documentStream, xmlDocumentLocation)
{
}
public BodySubstitutionParser(LinkContext context, Stream documentStream, EmbeddedResource resource, AssemblyDefinition resourceAssembly, string xmlDocumentLocation = "")
: base(context, documentStream, resource, resourceAssembly, xmlDocumentLocation)
{
}
public void Parse(SubstitutionInfo xmlInfo)
{
_substitutionInfo = xmlInfo;
bool stripSubstitutions = _context.IsOptimizationEnabled(CodeOptimizations.RemoveSubstitutions, _resource?.Assembly);
ProcessXml(stripSubstitutions, _context.IgnoreSubstitutions);
}
protected override void ProcessAssembly(AssemblyDefinition assembly, XPathNavigator nav, bool warnOnUnresolvedTypes)
{
ProcessTypes(assembly, nav, warnOnUnresolvedTypes);
ProcessResources(assembly, nav);
}
protected override TypeDefinition? ProcessExportedType(ExportedType exported, AssemblyDefinition assembly, XPathNavigator nav) => null;
protected override bool ProcessTypePattern(string fullname, AssemblyDefinition assembly, XPathNavigator nav) => false;
protected override void ProcessType(TypeDefinition type, XPathNavigator nav)
{
Debug.Assert(ShouldProcessElement(nav));
ProcessTypeChildren(type, nav);
}
protected override void ProcessMethod(TypeDefinition type, XPathNavigator methodNav, object? _customData)
{
Debug.Assert(_substitutionInfo != null);
string signature = GetSignature(methodNav);
if (string.IsNullOrEmpty(signature))
return;
MethodDefinition? method = FindMethod(type, signature);
if (method == null)
{
LogWarning(methodNav, DiagnosticId.XmlCouldNotFindMethodOnType, signature, type.GetDisplayName());
return;
}
string action = GetAttribute(methodNav, "body");
switch (action)
{
case "remove":
_substitutionInfo.SetMethodAction(method, MethodAction.ConvertToThrow);
return;
case "stub":
string value = GetAttribute(methodNav, "value");
if (!string.IsNullOrEmpty(value))
{
if (!TryConvertValue(value, method.ReturnType, out object? res))
{
LogWarning(methodNav, DiagnosticId.XmlInvalidValueForStub, method.GetDisplayName());
return;
}
_substitutionInfo.SetMethodStubValue(method, res);
}
_substitutionInfo.SetMethodAction(method, MethodAction.ConvertToStub);
return;
default:
LogWarning(methodNav, DiagnosticId.XmlUnkownBodyModification, action, method.GetDisplayName());
return;
}
}
protected override void ProcessField(TypeDefinition type, XPathNavigator fieldNav)
{
Debug.Assert(_substitutionInfo != null);
string name = GetAttribute(fieldNav, "name");
if (string.IsNullOrEmpty(name))
return;
var field = type.Fields.FirstOrDefault(f => f.Name == name);
if (field == null)
{
LogWarning(fieldNav, DiagnosticId.XmlCouldNotFindFieldOnType, name, type.GetDisplayName());
return;
}
if (!field.IsStatic || field.IsLiteral)
{
LogWarning(fieldNav, DiagnosticId.XmlSubstitutedFieldNeedsToBeStatic, field.GetDisplayName());
return;
}
string value = GetAttribute(fieldNav, "value");
if (string.IsNullOrEmpty(value))
{
LogWarning(fieldNav, DiagnosticId.XmlMissingSubstitutionValueForField, field.GetDisplayName());
return;
}
if (!TryConvertValue(value, field.FieldType, out object? res))
{
LogWarning(fieldNav, DiagnosticId.XmlInvalidSubstitutionValueForField, value, field.GetDisplayName());
return;
}
_substitutionInfo.SetFieldValue(field, res);
string init = GetAttribute(fieldNav, "initialize");
if (init?.ToLowerInvariant() == "true")
{
// We would need to also mess with the cctor of the type to set the field to this value,
// and doing so correctly is difficult.
throw new NotSupportedException();
}
}
void ProcessResources(AssemblyDefinition assembly, XPathNavigator nav)
{
foreach (XPathNavigator resourceNav in nav.SelectChildren("resource", ""))
{
if (!ShouldProcessElement(resourceNav))
continue;
string name = GetAttribute(resourceNav, "name");
if (string.IsNullOrEmpty(name))
{
LogWarning(resourceNav, DiagnosticId.XmlMissingNameAttributeInResource);
continue;
}
string action = GetAttribute(resourceNav, "action");
if (action != "remove")
{
LogWarning(resourceNav, DiagnosticId.XmlInvalidValueForAttributeActionForResource, action, name);
continue;
}
EmbeddedResource? resource = assembly.FindEmbeddedResource(name);
if (resource == null)
{
LogWarning(resourceNav, DiagnosticId.XmlCouldNotFindResourceToRemoveInAssembly, name, assembly.Name.Name);
continue;
}
_context.Annotations.AddResourceToRemove(assembly, resource);
}
}
static MethodDefinition? FindMethod(TypeDefinition type, string signature)
{
if (!type.HasMethods)
return null;
foreach (MethodDefinition meth in type.Methods)
if (signature == DescriptorMarker.GetMethodSignature(meth, includeGenericParameters: true))
return meth;
return null;
}
}
}
|