未验证 提交 24bbfbfb 编写于 作者: T Tlakaelel Axayakatl Ceja 提交者: GitHub

Add SubstitutionParser that uses XPath Navigator into NativeAOT (#64671)

上级 a7052ac7
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using Internal.IL;
using Internal.IL.Stubs;
using Internal.TypeSystem;
namespace ILCompiler
{
internal sealed class BodySubstitution
{
private object _value;
private readonly static object Throw = new object();
public readonly static BodySubstitution ThrowingBody = new BodySubstitution(Throw);
public readonly static BodySubstitution EmptyBody = new BodySubstitution(null);
public object Value
{
get
{
Debug.Assert(_value != Throw);
return _value;
}
}
private BodySubstitution(object value) => _value = value;
public static BodySubstitution Create(object value) => new BodySubstitution(value);
public MethodIL EmitIL(MethodDesc method)
{
ILEmitter emit = new ILEmitter();
ILCodeStream codestream = emit.NewCodeStream();
if (_value == Throw)
{
codestream.EmitCallThrowHelper(emit, method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowFeatureBodyRemoved"));
}
else if (_value == null)
{
Debug.Assert(method.Signature.ReturnType.IsVoid);
codestream.Emit(ILOpcode.ret);
}
else
{
Debug.Assert(_value is int);
codestream.EmitLdc((int)_value);
codestream.Emit(ILOpcode.ret);
}
return emit.Link(method);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection.Metadata;
using Internal.TypeSystem;
using System.Xml.XPath;
using System.Globalization;
using System.Linq;
namespace ILCompiler
{
internal sealed class BodySubstitutionsParser : ProcessLinkerXmlBase
{
private readonly Dictionary<MethodDesc, BodySubstitution> _methodSubstitutions;
private readonly Dictionary<FieldDesc, object> _fieldSubstitutions;
private BodySubstitutionsParser(TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
: base(context, documentStream, resource, resourceAssembly, xmlDocumentLocation, featureSwitchValues)
{
_methodSubstitutions = new Dictionary<MethodDesc, BodySubstitution>();
_fieldSubstitutions = new Dictionary<FieldDesc, object>();
}
protected override void ProcessAssembly(ModuleDesc assembly, XPathNavigator nav, bool warnOnUnresolvedTypes)
{
ProcessTypes(assembly, nav, warnOnUnresolvedTypes);
}
// protected override TypeDesc? ProcessExportedType(ExportedType exported, ModuleDesc assembly, XPathNavigator nav) => null;
protected override bool ProcessTypePattern(string fullname, ModuleDesc assembly, XPathNavigator nav) => false;
protected override void ProcessType(TypeDesc type, XPathNavigator nav)
{
Debug.Assert(ShouldProcessElement(nav));
ProcessTypeChildren(type, nav);
}
protected override void ProcessMethod(TypeDesc type, XPathNavigator methodNav, object customData)
{
string signature = GetSignature(methodNav);
if (string.IsNullOrEmpty(signature))
return;
MethodDesc method = FindMethod(type, signature);
if (method == null)
{
// LogWarning(methodNav, DiagnosticId.XmlCouldNotFindMethodOnType, signature, type.GetDisplayName());
return;
}
string action = GetAttribute(methodNav, "body");
switch (action)
{
case "remove":
_methodSubstitutions.Add(method, BodySubstitution.ThrowingBody);
break;
case "stub":
BodySubstitution stubBody;
if (method.Signature.ReturnType.IsVoid)
stubBody = BodySubstitution.EmptyBody;
else
stubBody = BodySubstitution.Create(TryCreateSubstitution(method.Signature.ReturnType, GetAttribute(methodNav, "value")));
if (stubBody != null)
{
_methodSubstitutions[method] = stubBody;
}
else
{
// Context.LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub", 2010, _xmlDocumentLocation);
}
break;
default:
//Context.LogWarning($"Unknown body modification '{action}' for '{method.GetDisplayName()}'", 2011, _xmlDocumentLocation);
break;
}
}
protected override void ProcessField(TypeDesc type, XPathNavigator fieldNav)
{
string name = GetAttribute(fieldNav, "name");
if (string.IsNullOrEmpty(name))
return;
var field = type.GetFields().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))
{
//Context.LogWarning($"Missing 'value' attribute for field '{field.GetDisplayName()}'.", 2014, _xmlDocumentLocation);
return;
}
object substitution = TryCreateSubstitution(field.FieldType, value);
if (substitution == null)
{
//Context.LogWarning($"Invalid value '{value}' for '{field.GetDisplayName()}'.", 2015, _xmlDocumentLocation);
return;
}
if (String.Equals(GetAttribute(fieldNav, "initialize"), "true", StringComparison.InvariantCultureIgnoreCase))
{
// We would need to also mess with the cctor of the type to set the field to this value:
//
// * Linker will remove all stsfld instructions referencing this field from the cctor
// * It will place an explicit stsfld in front of the last "ret" instruction in the cctor
//
// This approach... has issues.
throw new NotSupportedException();
}
_fieldSubstitutions[field] = substitution;
}
static MethodDesc FindMethod(TypeDesc type, string signature)
{
foreach (MethodDesc meth in type.GetMethods())
if (signature == GetMethodSignature(meth, includeGenericParameters: true))
return meth;
return null;
}
private object TryCreateSubstitution(TypeDesc type, string value)
{
switch (type.UnderlyingType.Category)
{
case TypeFlags.Int32:
if (string.IsNullOrEmpty(value))
return 0;
else if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int iresult))
return iresult;
break;
case TypeFlags.Boolean:
if (String.IsNullOrEmpty(value))
return 0;
else if (bool.TryParse(value, out bool bvalue))
return bvalue ? 1 : 0;
else
goto case TypeFlags.Int32;
default:
throw new NotSupportedException(type.ToString());
}
return null;
}
public static (Dictionary<MethodDesc, BodySubstitution>, Dictionary<FieldDesc, object>) GetSubstitutions(TypeSystemContext context, UnmanagedMemoryStream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
{
var rdr = new BodySubstitutionsParser(context, documentStream, resource, resourceAssembly, xmlDocumentLocation, featureSwitchValues);
rdr.ProcessXml(false);
return (rdr._methodSubstitutions, rdr._fieldSubstitutions);
}
}
}
......@@ -8,7 +8,6 @@
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Xml;
using Internal.IL;
using Internal.IL.Stubs;
using Internal.TypeSystem;
......@@ -740,172 +739,10 @@ public AssemblyFeatureInfo(EcmaModule module, IReadOnlyDictionary<string, bool>
ms = new UnmanagedMemoryStream(reader.CurrentPointer, length);
}
(BodySubstitutions, FieldSubstitutions) = SubstitutionsReader.GetSubstitutions(module.Context, XmlReader.Create(ms), module, featureSwitchValues);
(BodySubstitutions, FieldSubstitutions) = BodySubstitutionsParser.GetSubstitutions(module.Context, ms, resource, module, "name", featureSwitchValues);
}
}
}
}
private class BodySubstitution
{
private object _value;
private readonly static object Throw = new object();
public readonly static BodySubstitution ThrowingBody = new BodySubstitution(Throw);
public readonly static BodySubstitution EmptyBody = new BodySubstitution(null);
public object Value
{
get
{
Debug.Assert(_value != Throw);
return _value;
}
}
private BodySubstitution(object value) => _value = value;
public static BodySubstitution Create(object value) => new BodySubstitution(value);
public MethodIL EmitIL(MethodDesc method)
{
ILEmitter emit = new ILEmitter();
ILCodeStream codestream = emit.NewCodeStream();
if (_value == Throw)
{
codestream.EmitCallThrowHelper(emit, method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowFeatureBodyRemoved"));
}
else if (_value == null)
{
Debug.Assert(method.Signature.ReturnType.IsVoid);
codestream.Emit(ILOpcode.ret);
}
else
{
Debug.Assert(_value is int);
codestream.EmitLdc((int)_value);
codestream.Emit(ILOpcode.ret);
}
return emit.Link(method);
}
}
private class SubstitutionsReader : ProcessXmlBase
{
private readonly Dictionary<MethodDesc, BodySubstitution> _methodSubstitutions;
private readonly Dictionary<FieldDesc, object> _fieldSubstitutions;
private SubstitutionsReader(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary<string, bool> featureSwitchValues)
: base(context, reader, module, featureSwitchValues)
{
_methodSubstitutions = new Dictionary<MethodDesc, BodySubstitution>();
_fieldSubstitutions = new Dictionary<FieldDesc, object>();
}
protected override void ProcessMethod(MethodDesc method)
{
string action = GetAttribute("body");
if (!String.IsNullOrEmpty(action))
{
switch (action)
{
case "remove":
_methodSubstitutions.Add(method, BodySubstitution.ThrowingBody);
break;
case "stub":
BodySubstitution stubBody;
if (method.Signature.ReturnType.IsVoid)
stubBody = BodySubstitution.EmptyBody;
else
stubBody = BodySubstitution.Create(TryCreateSubstitution(method.Signature.ReturnType, GetAttribute("value")));
if (stubBody != null)
{
_methodSubstitutions[method] = stubBody;
}
else
{
// Context.LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub", 2010, _xmlDocumentLocation);
}
break;
default:
//Context.LogWarning($"Unknown body modification '{action}' for '{method.GetDisplayName()}'", 2011, _xmlDocumentLocation);
break;
}
}
}
protected override void ProcessField(FieldDesc field)
{
if (!field.IsStatic || field.IsLiteral)
{
// Context.LogWarning ($"Substituted field '{field.GetDisplayName ()}' needs to be static field.", 2013, _xmlDocumentLocation);
return;
}
string value = GetAttribute("value");
if (string.IsNullOrEmpty(value))
{
//Context.LogWarning($"Missing 'value' attribute for field '{field.GetDisplayName()}'.", 2014, _xmlDocumentLocation);
return;
}
object substitution = TryCreateSubstitution(field.FieldType, value);
if (substitution == null)
{
//Context.LogWarning($"Invalid value '{value}' for '{field.GetDisplayName()}'.", 2015, _xmlDocumentLocation);
return;
}
if (String.Equals(GetAttribute("initialize"), "true", StringComparison.InvariantCultureIgnoreCase))
{
// We would need to also mess with the cctor of the type to set the field to this value:
//
// * Linker will remove all stsfld instructions referencing this field from the cctor
// * It will place an explicit stsfld in front of the last "ret" instruction in the cctor
//
// This approach... has issues.
throw new NotSupportedException();
}
_fieldSubstitutions[field] = substitution;
}
private object TryCreateSubstitution(TypeDesc type, string value)
{
switch (type.UnderlyingType.Category)
{
case TypeFlags.Int32:
if (string.IsNullOrEmpty(value))
return 0;
else if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int iresult))
return iresult;
break;
case TypeFlags.Boolean:
if (String.IsNullOrEmpty(value))
return 0;
else if (bool.TryParse(value, out bool bvalue))
return bvalue ? 1 : 0;
else
goto case TypeFlags.Int32;
default:
throw new NotSupportedException(type.ToString());
}
return null;
}
public static (Dictionary<MethodDesc, BodySubstitution>, Dictionary<FieldDesc, object>) GetSubstitutions(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary<string, bool> featureSwitchValues)
{
var rdr = new SubstitutionsReader(context, reader, module, featureSwitchValues);
rdr.ProcessXml();
return (rdr._methodSubstitutions, rdr._fieldSubstitutions);
}
}
}
}
......@@ -50,7 +50,7 @@ public abstract class ProcessLinkerXmlBase
private readonly IReadOnlyDictionary<string, bool> _featureSwitchValues;
protected readonly TypeSystemContext _context;
protected ProcessLinkerXmlBase(TypeSystemContext context, UnmanagedMemoryStream documentStream, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
protected ProcessLinkerXmlBase(TypeSystemContext context, Stream documentStream, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
{
_context = context;
using (documentStream)
......@@ -61,7 +61,7 @@ protected ProcessLinkerXmlBase(TypeSystemContext context, UnmanagedMemoryStream
_featureSwitchValues = featureSwitchValues;
}
protected ProcessLinkerXmlBase(TypeSystemContext context, UnmanagedMemoryStream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
protected ProcessLinkerXmlBase(TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary<string, bool> featureSwitchValues)
: this(context, documentStream, xmlDocumentLocation, featureSwitchValues)
{
_owningModule = resourceAssembly ?? throw new ArgumentNullException(nameof(resourceAssembly));
......@@ -489,6 +489,31 @@ protected static string GetAttribute(XPathNavigator nav, string attribute)
return nav.GetAttribute(attribute, XmlNamespace);
}
public static string GetMethodSignature(MethodDesc meth, bool includeGenericParameters)
{
StringBuilder sb = new StringBuilder();
CecilTypeNameFormatter.Instance.AppendName(sb, meth.Signature.ReturnType);
sb.Append(' ');
sb.Append(meth.Name);
if (includeGenericParameters && meth.HasInstantiation)
{
sb.Append('`');
sb.Append(meth.Instantiation.Length);
}
sb.Append('(');
for (int i = 0; i < meth.Signature.Length; i++)
{
if (i > 0)
sb.Append(',');
CecilTypeNameFormatter.Instance.AppendName(sb, meth.Signature[i]);
}
sb.Append(')');
return sb.ToString();
}
#if false
protected MessageOrigin GetMessageOriginForPosition(XPathNavigator position)
{
......
......@@ -300,6 +300,8 @@
<Compile Include="Compiler\AnalysisBasedInteropStubManager.cs" />
<Compile Include="Compiler\AnalysisBasedMetadataManager.cs" />
<Compile Include="Compiler\BlockedInternalsBlockingPolicy.cs" />
<Compile Include="Compiler\BodySubstitution.cs" />
<Compile Include="Compiler\BodySubstitutionParser.cs" />
<Compile Include="Compiler\CompilationBuilder.Aot.cs" />
<Compile Include="Compiler\CompilationModuleGroup.Aot.cs" />
<Compile Include="Compiler\CompilerTypeSystemContext.Aot.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册