From 076f8c58984b009033204ad1dd9f86c691ae42fb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 11 Jul 2023 13:50:09 +0900 Subject: [PATCH] Add support to specify substitutions externally (#88230) ILLinker allows specifying substitutions on the command line, same as descriptors. This can be useful for various workarounds. Add support for this for ILC as well. --- .../tools/Common/Compiler/ProcessLinkerXmlBase.cs | 11 ++- .../DependencyGraphTests.cs | 2 +- .../Compiler/BodySubstitution.cs | 2 +- .../Compiler/BodySubstitutionParser.cs | 54 +++++++++++++- .../Compiler/FeatureSwitchManager.cs | 32 ++++---- .../Compiler/ManifestResourceBlockingPolicy.cs | 85 ++++++++++++++++++---- .../Compiler/UsageBasedMetadataManager.cs | 10 +-- .../tools/aot/ILCompiler/ILCompilerRootCommand.cs | 5 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 24 +++++- .../TestCasesRunner/ILCompilerDriver.cs | 6 +- .../TrimmingBehaviors/FeatureSwitches.cs | 28 +++++++ .../TrimmingBehaviors/ILLink.Substitutions.xml | 2 + .../NonEmbedded.ILLink.Substitutions1.xml | 8 ++ .../NonEmbedded.ILLink.Substitutions2.xml | 5 ++ .../TrimmingBehaviors/TrimmingBehaviors.csproj | 20 +++++ 15 files changed, 244 insertions(+), 50 deletions(-) create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions1.xml create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions2.xml diff --git a/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs b/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs index eb07ab4..abf7ac8 100644 --- a/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs +++ b/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs @@ -10,9 +10,7 @@ using System.Reflection; using System.Reflection.Metadata; using System.Text; using System.Text.RegularExpressions; -#if !READYTORUN using System.Xml; -#endif using System.Xml.Linq; using System.Xml.XPath; @@ -59,6 +57,15 @@ namespace ILCompiler private readonly IReadOnlyDictionary _featureSwitchValues; protected readonly TypeSystemContext _context; + protected ProcessLinkerXmlBase(Logger logger, TypeSystemContext context, XmlReader xmlReader, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + { + _context = context; + _logger = logger; + _document = XDocument.Load(xmlReader, LoadOptions.SetLineInfo).CreateNavigator(); + _xmlDocumentLocation = xmlDocumentLocation; + _featureSwitchValues = featureSwitchValues; + } + protected ProcessLinkerXmlBase(Logger logger, TypeSystemContext context, Stream documentStream, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) { _context = context; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index 01cbbc5..c20912e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ namespace ILCompiler.Compiler.Tests new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - default, Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); + default, Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitution.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitution.cs index 769f088..6e2ce17 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitution.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitution.cs @@ -8,7 +8,7 @@ using Internal.TypeSystem; namespace ILCompiler { - internal sealed class BodySubstitution + public sealed class BodySubstitution { private object _value; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs index ed0dda8..8903274 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Reflection.Metadata; using Internal.TypeSystem; +using System.Xml; using System.Xml.XPath; using System.Globalization; using System.Linq; @@ -14,7 +15,7 @@ using ILLink.Shared; namespace ILCompiler { - internal sealed class BodySubstitutionsParser : ProcessLinkerXmlBase + public sealed class BodySubstitutionsParser : ProcessLinkerXmlBase { private readonly Dictionary _methodSubstitutions; private readonly Dictionary _fieldSubstitutions; @@ -27,6 +28,13 @@ namespace ILCompiler _fieldSubstitutions = new Dictionary(); } + private BodySubstitutionsParser(Logger logger, TypeSystemContext context, XmlReader document, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + : base(logger, context, document, xmlDocumentLocation, featureSwitchValues) + { + _methodSubstitutions = new Dictionary(); + _fieldSubstitutions = new Dictionary(); + } + protected override void ProcessAssembly(ModuleDesc assembly, XPathNavigator nav, bool warnOnUnresolvedTypes) { ProcessTypes(assembly, nav, warnOnUnresolvedTypes); @@ -178,11 +186,51 @@ namespace ILCompiler return null; } - public static (Dictionary, Dictionary) GetSubstitutions(Logger logger, TypeSystemContext context, UnmanagedMemoryStream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + public static BodyAndFieldSubstitutions GetSubstitutions(Logger logger, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) { var rdr = new BodySubstitutionsParser(logger, context, documentStream, resource, resourceAssembly, xmlDocumentLocation, featureSwitchValues); rdr.ProcessXml(false); - return (rdr._methodSubstitutions, rdr._fieldSubstitutions); + return new BodyAndFieldSubstitutions(rdr._methodSubstitutions, rdr._fieldSubstitutions); + } + + public static BodyAndFieldSubstitutions GetSubstitutions(Logger logger, TypeSystemContext context, XmlReader reader, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + { + var rdr = new BodySubstitutionsParser(logger, context, reader, xmlDocumentLocation, featureSwitchValues); + rdr.ProcessXml(false); + return new BodyAndFieldSubstitutions(rdr._methodSubstitutions, rdr._fieldSubstitutions); + } + } + + public struct BodyAndFieldSubstitutions + { + private Dictionary _bodySubstitutions; + private Dictionary _fieldSubstitutions; + + public IReadOnlyDictionary BodySubstitutions => _bodySubstitutions; + public IReadOnlyDictionary FieldSubstitutions => _fieldSubstitutions; + + public BodyAndFieldSubstitutions(Dictionary bodySubstitutions, Dictionary fieldSubstitutions) + => (_bodySubstitutions, _fieldSubstitutions) = (bodySubstitutions, fieldSubstitutions); + + public void AppendFrom(BodyAndFieldSubstitutions other) + { + if (_bodySubstitutions == null) + { + _bodySubstitutions = other._bodySubstitutions; + _fieldSubstitutions = other._fieldSubstitutions; + } + else if (other._bodySubstitutions == null) + { + // Nothing to do + } + else + { + foreach (var kvp in other._bodySubstitutions) + _bodySubstitutions[kvp.Key] = kvp.Value; + + foreach (var kvp in other._fieldSubstitutions) + _fieldSubstitutions[kvp.Key] = kvp.Value; + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs index a1dfaec..2b2617b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs @@ -25,16 +25,10 @@ namespace ILCompiler private readonly FeatureSwitchHashtable _hashtable; private readonly ILProvider _nestedILProvider; - public FeatureSwitchManager(ILProvider nestedILProvider, Logger logger, IEnumerable> switchValues) + public FeatureSwitchManager(ILProvider nestedILProvider, Logger logger, IReadOnlyDictionary switchValues, BodyAndFieldSubstitutions globalSubstitutions) { _nestedILProvider = nestedILProvider; - - // Last setting wins - var dictionary = new Dictionary(); - foreach ((string name, bool value) in switchValues) - dictionary[name] = value; - - _hashtable = new FeatureSwitchHashtable(logger, dictionary); + _hashtable = new FeatureSwitchHashtable(logger, switchValues, globalSubstitutions); } private BodySubstitution GetSubstitution(MethodDesc method) @@ -895,13 +889,15 @@ namespace ILCompiler private sealed class FeatureSwitchHashtable : LockFreeReaderHashtable { - internal readonly Dictionary _switchValues; + internal readonly IReadOnlyDictionary _switchValues; private readonly Logger _logger; + private readonly BodyAndFieldSubstitutions _globalSubstitutions; - public FeatureSwitchHashtable(Logger logger, Dictionary switchValues) + public FeatureSwitchHashtable(Logger logger, IReadOnlyDictionary switchValues, BodyAndFieldSubstitutions globalSubstitutions) { _logger = logger; _switchValues = switchValues; + _globalSubstitutions = globalSubstitutions; } protected override bool CompareKeyToValue(EcmaModule key, AssemblyFeatureInfo value) => key == value.Module; @@ -911,7 +907,7 @@ namespace ILCompiler protected override AssemblyFeatureInfo CreateValueFromKey(EcmaModule key) { - return new AssemblyFeatureInfo(key, _logger, _switchValues); + return new AssemblyFeatureInfo(key, _logger, _switchValues, _globalSubstitutions); } } @@ -919,11 +915,11 @@ namespace ILCompiler { public EcmaModule Module { get; } - public Dictionary BodySubstitutions { get; } - public Dictionary FieldSubstitutions { get; } + public IReadOnlyDictionary BodySubstitutions { get; } + public IReadOnlyDictionary FieldSubstitutions { get; } public Dictionary InlineableResourceStrings { get; } - public AssemblyFeatureInfo(EcmaModule module, Logger logger, IReadOnlyDictionary featureSwitchValues) + public AssemblyFeatureInfo(EcmaModule module, Logger logger, IReadOnlyDictionary featureSwitchValues, BodyAndFieldSubstitutions globalSubstitutions) { Module = module; @@ -951,7 +947,13 @@ namespace ILCompiler ms = new UnmanagedMemoryStream(reader.CurrentPointer, length); } - (BodySubstitutions, FieldSubstitutions) = BodySubstitutionsParser.GetSubstitutions(logger, module.Context, ms, resource, module, "name", featureSwitchValues); + BodyAndFieldSubstitutions substitutions = BodySubstitutionsParser.GetSubstitutions(logger, module.Context, ms, resource, module, "name", featureSwitchValues); + + // Also apply any global substitutions + // Note we allow these to overwrite substitutions in the assembly + substitutions.AppendFrom(globalSubstitutions); + + (BodySubstitutions, FieldSubstitutions) = (substitutions.BodySubstitutions, substitutions.FieldSubstitutions); } else if (InlineableStringsResourceNode.IsInlineableStringsResource(module, resourceName)) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs index a76f62b..8961a4a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using System.Xml; using System.Xml.XPath; using Internal.TypeSystem; @@ -23,9 +24,9 @@ namespace ILCompiler protected ManifestResourceBlockingPolicy() { } - public ManifestResourceBlockingPolicy(Logger logger, IEnumerable> switchValues) + public ManifestResourceBlockingPolicy(Logger logger, IReadOnlyDictionary switchValues, IReadOnlyDictionary> globalBlocks) { - _hashtable = new FeatureSwitchHashtable(logger, new Dictionary(switchValues)); + _hashtable = new FeatureSwitchHashtable(logger, switchValues, globalBlocks); } /// @@ -39,15 +40,47 @@ namespace ILCompiler || (resourceName.StartsWith("ILLink.") && resourceName.EndsWith(".xml"))); } + public static IReadOnlyDictionary> UnionBlockings(IReadOnlyDictionary> left, IReadOnlyDictionary> right) + { + var result = new Dictionary>(); + if (left != null) + AddAll(result, left); + if (right != null) + AddAll(result, right); + + return AsReadOnlyDictionary(result); + + static void AddAll(Dictionary> result, IReadOnlyDictionary> addend) + { + foreach (var item in addend) + { + if (!result.TryGetValue(item.Key, out HashSet set)) + result.Add(item.Key, set = new HashSet()); + set.UnionWith(item.Value); + } + } + } + + private static Dictionary> AsReadOnlyDictionary(Dictionary> original) + { + // IReadOnlyDictionary is not variant, so we need to: + var copy = new Dictionary>(); + foreach (KeyValuePair> moduleSet in original) + copy.Add(moduleSet.Key, moduleSet.Value); + return copy; + } + private sealed class FeatureSwitchHashtable : LockFreeReaderHashtable { - private readonly Dictionary _switchValues; + private readonly IReadOnlyDictionary _switchValues; + private readonly IReadOnlyDictionary> _globalBlocks; private readonly Logger _logger; - public FeatureSwitchHashtable(Logger logger, Dictionary switchValues) + public FeatureSwitchHashtable(Logger logger, IReadOnlyDictionary switchValues, IReadOnlyDictionary> globalBlocks) { _logger = logger; _switchValues = switchValues; + _globalBlocks = globalBlocks; } protected override bool CompareKeyToValue(EcmaModule key, AssemblyFeatureInfo value) => key == value.Module; @@ -57,7 +90,7 @@ namespace ILCompiler protected override AssemblyFeatureInfo CreateValueFromKey(EcmaModule key) { - return new AssemblyFeatureInfo(key, _logger, _switchValues); + return new AssemblyFeatureInfo(key, _logger, _switchValues, _globalBlocks); } } @@ -65,9 +98,9 @@ namespace ILCompiler { public EcmaModule Module { get; } - public HashSet BlockedResources { get; } + public IReadOnlySet BlockedResources { get; } - public AssemblyFeatureInfo(EcmaModule module, Logger logger, IReadOnlyDictionary featureSwitchValues) + public AssemblyFeatureInfo(EcmaModule module, Logger logger, IReadOnlyDictionary featureSwitchValues, IReadOnlyDictionary> globalBlocks) { Module = module; BlockedResources = new HashSet(); @@ -96,29 +129,49 @@ namespace ILCompiler ms = new UnmanagedMemoryStream(reader.CurrentPointer, length); } - BlockedResources = SubstitutionsReader.GetSubstitutions(logger, module.Context, ms, resource, module, "resource " + resourceName + " in " + module.ToString(), featureSwitchValues); + BlockedResources = SubstitutionsReader.GetSubstitutions(logger, module.Context, ms, resource, module, "resource " + resourceName + " in " + module.ToString(), featureSwitchValues)[module]; } } + + if (globalBlocks != null && globalBlocks.TryGetValue(module, out IReadOnlySet fromGlobal)) + { + var result = new HashSet(fromGlobal); + result.UnionWith(BlockedResources); + BlockedResources = result; + } } } - private sealed class SubstitutionsReader : ProcessLinkerXmlBase + public sealed class SubstitutionsReader : ProcessLinkerXmlBase { - private readonly HashSet _substitutions = new(); + private readonly Dictionary> _substitutions = new(); private SubstitutionsReader(Logger logger, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) : base(logger, context, documentStream, resource, resourceAssembly, xmlDocumentLocation, featureSwitchValues) { + _substitutions.Add(resourceAssembly, new HashSet()); } - public static HashSet GetSubstitutions(Logger logger, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + private SubstitutionsReader(Logger logger, TypeSystemContext context, XmlReader document, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + : base(logger, context, document, xmlDocumentLocation, featureSwitchValues) + { + } + + public static IReadOnlyDictionary> GetSubstitutions(Logger logger, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc resourceAssembly, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) { var rdr = new SubstitutionsReader(logger, context, documentStream, resource, resourceAssembly, xmlDocumentLocation, featureSwitchValues); rdr.ProcessXml(false); - return rdr._substitutions; + return AsReadOnlyDictionary(rdr._substitutions); + } + + public static IReadOnlyDictionary> GetSubstitutions(Logger logger, TypeSystemContext context, XmlReader document, string xmlDocumentLocation, IReadOnlyDictionary featureSwitchValues) + { + var rdr = new SubstitutionsReader(logger, context, document, xmlDocumentLocation, featureSwitchValues); + rdr.ProcessXml(false); + return AsReadOnlyDictionary(rdr._substitutions); } - private void ProcessResources(XPathNavigator nav) + private void ProcessResources(ModuleDesc assembly, XPathNavigator nav) { foreach (XPathNavigator resourceNav in nav.SelectChildren("resource", "")) { @@ -139,13 +192,15 @@ namespace ILCompiler continue; } - _substitutions.Add(name); + if (!_substitutions.TryGetValue(assembly, out HashSet removed)) + _substitutions.Add(assembly, removed = new HashSet()); + removed.Add(name); } } protected override void ProcessAssembly(ModuleDesc assembly, XPathNavigator nav, bool warnOnUnresolvedTypes) { - ProcessResources(nav); + ProcessResources(assembly, nav); } // Should not be resolving types. That's useless work. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 1410412..39d06a4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -81,7 +81,7 @@ namespace ILCompiler UsageBasedMetadataGenerationOptions generationOptions, MetadataManagerOptions options, Logger logger, - IEnumerable> featureSwitchValues, + IReadOnlyDictionary featureSwitchValues, IEnumerable rootEntireAssembliesModules, IEnumerable additionalRootedAssemblies, IEnumerable trimmedAssemblies, @@ -94,8 +94,8 @@ namespace ILCompiler FlowAnnotations = flowAnnotations; Logger = logger; - _linkAttributesHashTable = new LinkAttributesHashTable(Logger, new Dictionary(featureSwitchValues)); - FeatureSwitches = new Dictionary(featureSwitchValues); + _linkAttributesHashTable = new LinkAttributesHashTable(Logger, featureSwitchValues); + FeatureSwitches = featureSwitchValues; _rootEntireAssembliesModules = new HashSet(rootEntireAssembliesModules); _rootEntireAssembliesModules.UnionWith(additionalRootedAssemblies); @@ -979,10 +979,10 @@ namespace ILCompiler private sealed class LinkAttributesHashTable : LockFreeReaderHashtable { - private readonly Dictionary _switchValues; + private readonly IReadOnlyDictionary _switchValues; private readonly Logger _logger; - public LinkAttributesHashTable(Logger logger, Dictionary switchValues) + public LinkAttributesHashTable(Logger logger, IReadOnlyDictionary switchValues) { _logger = logger; _switchValues = switchValues; diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 6ac3e5a..42db74d 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -62,7 +62,9 @@ namespace ILCompiler public CliOption RdXmlFilePaths { get; } = new("--rdxml") { DefaultValueFactory = _ => Array.Empty(), Description = "RD.XML file(s) for compilation" }; public CliOption LinkTrimFilePaths { get; } = - new("--descriptor") { DefaultValueFactory = _ => Array.Empty(), Description = "ILLinkTrim.Descriptor file(s) for compilation" }; + new("--descriptor") { DefaultValueFactory = _ => Array.Empty(), Description = "ILLink.Descriptor file(s) for compilation" }; + public CliOption SubstitutionFilePaths { get; } = + new("--substitution") { DefaultValueFactory = _ => Array.Empty(), Description = "ILLink.Substitution file(s) for compilation" }; public CliOption MapFileName { get; } = new("--map") { Description = "Generate a map file" }; public CliOption MstatFileName { get; } = @@ -184,6 +186,7 @@ namespace ILCompiler Options.Add(CodegenOptions); Options.Add(RdXmlFilePaths); Options.Add(LinkTrimFilePaths); + Options.Add(SubstitutionFilePaths); Options.Add(MapFileName); Options.Add(MstatFileName); Options.Add(MetadataLogFileName); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index db1160a..0355885 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -13,6 +13,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Xml; using Internal.IL; using Internal.TypeSystem; @@ -327,17 +328,32 @@ namespace ILCompiler var logger = new Logger(Console.Out, ilProvider, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies), suppressedWarningCategories); - List> featureSwitches = new List>(); + var featureSwitches = new Dictionary(); foreach (var switchPair in Get(_command.FeatureSwitches)) { string[] switchAndValue = switchPair.Split('='); if (switchAndValue.Length != 2 || !bool.TryParse(switchAndValue[1], out bool switchValue)) throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); - featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); + featureSwitches[switchAndValue[0]] = switchValue; } - ilProvider = new FeatureSwitchManager(ilProvider, logger, featureSwitches); + BodyAndFieldSubstitutions substitutions = default; + IReadOnlyDictionary> resourceBlocks = default; + foreach (string substitutionFilePath in Get(_command.SubstitutionFilePaths)) + { + using FileStream fs = File.OpenRead(substitutionFilePath); + substitutions.AppendFrom(BodySubstitutionsParser.GetSubstitutions( + logger, typeSystemContext, XmlReader.Create(fs), substitutionFilePath, featureSwitches)); + + fs.Seek(0, SeekOrigin.Begin); + + resourceBlocks = ManifestResourceBlockingPolicy.UnionBlockings(resourceBlocks, + ManifestResourceBlockingPolicy.SubstitutionsReader.GetSubstitutions( + logger, typeSystemContext, XmlReader.Create(fs), substitutionFilePath, featureSwitches)); + } + + ilProvider = new FeatureSwitchManager(ilProvider, logger, featureSwitches, substitutions); CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState(ilProvider, logger); @@ -351,7 +367,7 @@ namespace ILCompiler { mdBlockingPolicy = new NoMetadataBlockingPolicy(); - resBlockingPolicy = new ManifestResourceBlockingPolicy(logger, featureSwitches); + resBlockingPolicy = new ManifestResourceBlockingPolicy(logger, featureSwitches, resourceBlocks); metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; if (Get(_command.CompleteTypesMetadata)) diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index 304ad1f..275d035 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -92,7 +92,7 @@ namespace Mono.Linker.Tests.TestCasesRunner compilationRoots.Add (new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode (descriptor)); } - ilProvider = new FeatureSwitchManager (ilProvider, logger, options.FeatureSwitches); + ilProvider = new FeatureSwitchManager (ilProvider, logger, options.FeatureSwitches, default); CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState (ilProvider, logger); @@ -100,7 +100,7 @@ namespace Mono.Linker.Tests.TestCasesRunner compilationGroup, typeSystemContext, new NoMetadataBlockingPolicy (), - new ManifestResourceBlockingPolicy (logger, options.FeatureSwitches), + new ManifestResourceBlockingPolicy (logger, options.FeatureSwitches, new Dictionary>()), logFile: null, new NoStackTraceEmissionPolicy (), new NoDynamicInvokeThunkGenerationPolicy (), @@ -108,7 +108,7 @@ namespace Mono.Linker.Tests.TestCasesRunner UsageBasedMetadataGenerationOptions.ReflectionILScanning, options: default, logger, - Array.Empty> (), + options.FeatureSwitches, Array.Empty (), options.AdditionalRootAssemblies.ToArray (), options.TrimAssemblies.ToArray (), diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/FeatureSwitches.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/FeatureSwitches.cs index aa0b781..4640774 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/FeatureSwitches.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/FeatureSwitches.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection; using System.Diagnostics.CodeAnalysis; using BindingFlags = System.Reflection.BindingFlags; @@ -14,6 +15,9 @@ class FeatureSwitches static int GetIntConstant() => 0; static bool s_isEnabled = IsEnabled(); + // These are substituted by embedded XML file and re-substituted by XML passed on command line + static int GetCookie() => 0; + public static int Run() { SanityTest.Run(); @@ -22,6 +26,8 @@ class FeatureSwitches TestEmptyFinally.Run(); TestStaticField.Run(); TestIntConstant.Run(); + TestResubstitution.Run(); + TestResourceStripping.Run(); return 100; } @@ -149,6 +155,28 @@ class FeatureSwitches } } + class TestResubstitution + { + public static void Run() + { + if (GetCookie() != 100) + throw new Exception(GetCookie().ToString()); + } + } + + class TestResourceStripping + { + public static void Run() + { + string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); + Console.WriteLine("Resource list"); + foreach (string n in names) + Console.WriteLine(n); + if (names.Length != 1 || names[0] != "KeptResource") + throw new Exception(); + } + } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "That's the point")] private static bool IsTypePresent(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public) != null; diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLink.Substitutions.xml b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLink.Substitutions.xml index 80f928d..28a39ad 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLink.Substitutions.xml +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/ILLink.Substitutions.xml @@ -3,5 +3,7 @@ + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions1.xml b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions1.xml new file mode 100644 index 0000000..df9ecf7 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions1.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions2.xml b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions2.xml new file mode 100644 index 0000000..a0e7c53 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/NonEmbedded.ILLink.Substitutions2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj index f8bc1ef..4eb4532 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj @@ -35,4 +35,24 @@ + + + + + + + + RemovedResource1 + + + RemovedResource2 + + + RemovedResource3 + + + KeptResource + + + -- 2.7.4