diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml index 5cc8e589f0faa..991377561048d 100644 --- a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml @@ -139,18 +139,6 @@ member M:System.Xml.Serialization.XmlReflectionImporter.GetMethodFromSchemaProvider(System.Xml.Serialization.XmlSchemaProviderAttribute,System.Type) - - ILLink - IL2070 - member - M:System.Xml.Xsl.Runtime.EarlyBoundInfo.#ctor(System.String,System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Xsl.XslCompiledTransform.Load(System.Type) - ILLink IL2072 @@ -175,12 +163,6 @@ member M:System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializer(System.String,System.String,System.Xml.Serialization.XmlMapping,System.Xml.Serialization.CodeIdentifiers,System.String,System.String,System.String) - - ILLink - IL2072 - member - M:System.Xml.Xsl.XsltOld.Processor.#ctor(System.Xml.XPath.XPathNavigator,System.Xml.Xsl.XsltArgumentList,System.Xml.XmlResolver,System.Xml.Xsl.XsltOld.Stylesheet,System.Collections.Generic.List{System.Xml.Xsl.XsltOld.TheQuery},System.Xml.Xsl.XsltOld.RootAction,System.Xml.Xsl.XsltOld.Debugger.IXsltDebugger) - ILLink IL2075 @@ -319,18 +301,6 @@ member M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteElement(System.Xml.Serialization.SourceInfo,System.Xml.Serialization.ElementAccessor,System.String,System.Boolean) - - ILLink - IL2075 - member - M:System.Xml.Xsl.IlGen.XmlILModule.BakeMethods - - - ILLink - IL2075 - member - M:System.Xml.Xsl.XsltOld.XsltCompileContext.GetExtentionMethod(System.String,System.String,System.Xml.XPath.XPathResultType[],System.Object@) - ILLink IL2077 @@ -345,15 +315,21 @@ ILLink - IL2080 + IL2067 member - M:System.Xml.Xsl.Runtime.XmlExtensionFunction.Bind + M:System.Xml.Xsl.Runtime.XmlExtensionFunction.#ctor(System.String,System.String,System.Int32,System.Type,System.Reflection.BindingFlags) ILLink - IL2080 + IL2067 member - M:System.Xml.Xsl.Runtime.XmlExtensionFunction.CanBind + M:System.Xml.Xsl.Runtime.XmlExtensionFunctionTable.Bind(System.String,System.String,System.Int32,System.Type,System.Reflection.BindingFlags) + + + ILLink + IL2075 + member + M:System.Xml.Xsl.XsltOld.XsltCompileContext.GetExtentionMethod(System.String,System.String,System.Xml.XPath.XPathResultType[],System.Object@) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs index c9e25cdad922b..8a163206ea3a4 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Xml.Xsl.Qil; using System.Xml.Xsl.Runtime; @@ -172,7 +173,7 @@ public string[]? GlobalNames /// Add early bound information to a list that is used by this query. Return the index of /// the early bound information in the list. /// - public int DeclareEarlyBound(string namespaceUri, Type ebType) + public int DeclareEarlyBound(string namespaceUri, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type ebType) { if (_earlyInfo == null) _earlyInfo = new UniqueList(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs index 10443b3ef29ea..49e6f3df25e30 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs @@ -35,6 +35,9 @@ internal class XmlILVisitor : QilVisitor private IteratorDescriptor? _iterNested; private int _indexId; + [RequiresUnreferencedCode("Method VisitXsltInvokeEarlyBound will require code that cannot be statically analyzed.")] + public XmlILVisitor() + { } //----------------------------------------------- // Entry @@ -3597,6 +3600,9 @@ protected override QilNode VisitXsltInvokeLateBound(QilInvokeLateBound ndInvoke) /// /// Generate code for QilNodeType.XsltInvokeEarlyBound. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since we added " + + "the attribute to this subclass' constructor. This allows us to not have to annotate the whole QilNode hirerarchy.")] protected override QilNode VisitXsltInvokeEarlyBound(QilInvokeEarlyBound ndInvoke) { QilName ndName = ndInvoke.Name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs index 9b44a9fe557d9..b63284285b45c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs @@ -3,6 +3,7 @@ #nullable disable using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.Xml.Xsl.Runtime @@ -14,13 +15,16 @@ internal sealed class EarlyBoundInfo { private readonly string _namespaceUri; // Namespace Uri mapped to these early bound functions private readonly ConstructorInfo _constrInfo; // Constructor for the early bound function object + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + private readonly Type _ebType; - public EarlyBoundInfo(string namespaceUri, Type ebType) + public EarlyBoundInfo(string namespaceUri, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type ebType) { Debug.Assert(namespaceUri != null && ebType != null); // Get the default constructor _namespaceUri = namespaceUri; + _ebType = ebType; _constrInfo = ebType.GetConstructor(Type.EmptyTypes); Debug.Assert(_constrInfo != null, "The early bound object type " + ebType.FullName + " must have a public default constructor"); } @@ -33,7 +37,11 @@ public EarlyBoundInfo(string namespaceUri, Type ebType) /// /// Return the Clr Type of the early bound object. /// - public Type EarlyBoundType { get { return _constrInfo.DeclaringType; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + public Type EarlyBoundType + { + get { return _ebType; } + } /// /// Create an instance of the early bound object. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs index 1707aa9d56099..6340197f07011 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Globalization; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl.Runtime { @@ -57,6 +58,7 @@ internal class XmlExtensionFunction private string _namespaceUri; // Extension object identifier private string _name; // Name of this method private int _numArgs; // Argument count + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] private Type _objectType; // Type of the object which will be searched for matching methods private BindingFlags _flags; // Modifiers that were used to search for a matching signature private int _hashCode; // Pre-computed hashcode @@ -95,7 +97,7 @@ public XmlExtensionFunction(string name, string namespaceUri, int numArgs, Type /// /// Initialize, but do not bind. /// - public void Init(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public void Init(string name, string namespaceUri, int numArgs, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicMethods)] Type objectType, BindingFlags flags) { _name = name; _namespaceUri = namespaceUri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs index 6f0fd48703b77..53387597f2db4 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml.Xsl.IlGen; using System.Xml.Xsl.Qil; @@ -35,6 +36,7 @@ internal class XmlQueryStaticData /// /// Constructor. /// + [RequiresUnreferencedCode("This method will create a copy that uses earlybound types which cannot be statically analyzed.")] public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList whitespaceRules, StaticDataManager staticData) { Debug.Assert(defaultWriterSettings != null && staticData != null); @@ -70,6 +72,7 @@ public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList /// Deserialize XmlQueryStaticData object from a byte array. /// + [RequiresUnreferencedCode("This method will create EarlyBoundInfo from passed in ebTypes array which cannot be statically analyzed.")] public XmlQueryStaticData(byte[] data, Type[] ebTypes) { MemoryStream dataStream = new MemoryStream(data, /*writable:*/false); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs index 610a883ecfb0e..0e4272fb3d899 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs @@ -70,6 +70,10 @@ public XmlILGenerator() // SxS Note: The way the trace file names are created (hardcoded) is NOT SxS safe. However the files are // created only for internal tracing purposes. In addition XmlILTrace class is not compiled into retail // builds. As a result it is fine to suppress the FxCop SxS warning. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This method will generate the IL methods using RefEmit at runtime, which will then try to call them " + + "using methods that are annotated as RequiresUnreferencedCode. In this case, these uses can be suppressed as the " + + "trimmer won't be able to trim any IL that gets generated at runtime.")] public XmlILCommand? Generate(QilExpression query, TypeBuilder? typeBldr) { _qil = query; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs index d7d3d3740534d..8f3526024e0ae 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs @@ -196,13 +196,14 @@ private QilExpression Compile(Compiler compiler) } // Create list of all early bound objects - Dictionary scriptClasses = compiler.Scripts.ScriptClasses; + Scripts.TrimSafeDictionary scriptClasses = compiler.Scripts.ScriptClasses; List ebTypes = new List(scriptClasses.Count); - foreach (KeyValuePair pair in scriptClasses) + foreach (string key in scriptClasses.Keys) { - if (pair.Value != null) + Type? value = scriptClasses[key]; + if (value != null) { - ebTypes.Add(new EarlyBoundInfo(pair.Key, pair.Value)); + ebTypes.Add(new EarlyBoundInfo(key, value)); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs index cbc8e315b3a92..d486470c823df 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs @@ -4,7 +4,9 @@ // http://devdiv/Documents/Whidbey/CLR/CurrentSpecs/BCL/CodeDom%20Activation.doc //------------------------------------------------------------------------------ +using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Xml.Xsl.Runtime; namespace System.Xml.Xsl.Xslt @@ -12,7 +14,7 @@ namespace System.Xml.Xsl.Xslt internal class Scripts { private readonly Compiler _compiler; - private readonly Dictionary _nsToType = new Dictionary(); + private readonly TrimSafeDictionary _nsToType = new TrimSafeDictionary(); private readonly XmlExtensionFunctionTable _extFuncs = new XmlExtensionFunctionTable(); public Scripts(Compiler compiler) @@ -20,7 +22,7 @@ public Scripts(Compiler compiler) _compiler = compiler; } - public Dictionary ScriptClasses + public TrimSafeDictionary ScriptClasses { get { return _nsToType; } } @@ -41,5 +43,29 @@ public Scripts(Compiler compiler) } return null; } + + internal class TrimSafeDictionary + { + private readonly Dictionary _backingDictionary = new Dictionary(); + + public Type? this[string key] + { + [UnconditionalSuppressMessage("TrimAnalysis", "IL2073:MissingDynamicallyAccessedMembers", + Justification = "The getter of the dictionary is not annotated to preserve the constructor, but the sources that are adding the items to " + + "the dictionary are annotated so we can supress the message as we know the constructor will be preserved.")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + get => _backingDictionary[key]; + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + set => _backingDictionary[key] = value; + } + + public ICollection Keys => _backingDictionary.Keys; + + public int Count => _backingDictionary.Count; + + public bool ContainsKey(string key) => _backingDictionary.ContainsKey(key); + + public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type? value) => _backingDictionary.TryGetValue(key, out value); + } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs index 8ef9fb4acef42..e9274d40a16a7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs @@ -371,15 +371,10 @@ public Processor( _scriptExtensions = new Hashtable(_stylesheet.ScriptObjectTypes.Count); { - foreach (DictionaryEntry entry in _stylesheet.ScriptObjectTypes) + // Scripts are not supported on stylesheets + if (_stylesheet.ScriptObjectTypes.Count > 0) { - string namespaceUri = (string)entry.Key; - if (GetExtensionObject(namespaceUri) != null) - { - throw XsltException.Create(SR.Xslt_ScriptDub, namespaceUri); - } - _scriptExtensions.Add(namespaceUri, Activator.CreateInstance((Type)entry.Value!, - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, null)); + throw new PlatformNotSupportedException(SR.CompilingScriptsNotSupported); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs index 6ed5b7ace1a74..f9abc50023f24 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs @@ -194,7 +194,7 @@ private void CompileQilToMsil(XsltSettings settings) //------------------------------------------------ // Load compiled stylesheet from a Type //------------------------------------------------ - + [RequiresUnreferencedCode("This method will get fields and types from the assembly of the passed in compiledStylesheet and call their constructors which cannot be statically analyzed")] public void Load(Type compiledStylesheet) { Reset(); @@ -238,6 +238,7 @@ public void Load(Type compiledStylesheet) throw new ArgumentException(SR.Format(SR.Xslt_NotCompiledStylesheet, compiledStylesheet.FullName), nameof(compiledStylesheet)); } + [RequiresUnreferencedCode("This method will call into constructors of the earlyBoundTypes array which cannot be statically analyzed.")] public void Load(MethodInfo executeMethod, byte[] queryData, Type[]? earlyBoundTypes) { Reset(); diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs index 2a71cb87072ef..141c830925b1b 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs @@ -871,6 +871,34 @@ public void LoadGeneric12(InputType inputType, ReaderType readerType) _output.WriteLine("Did not throw compile exception for stylesheet"); Assert.True(false); } + + [Fact] + public void XslTransformThrowsPNSEWhenUsingScripts() + { + using StringReader xslFile = new StringReader( + @" + + + + + + + + +"); + + using XmlReader reader = XmlReader.Create(xslFile); + XslTransform xslt = new XslTransform(); + XsltCompileException compilationException = Assert.Throws(() => xslt.Load(reader)); + Assert.True(compilationException.InnerException != null && compilationException.InnerException is PlatformNotSupportedException); + } } /**************************************************************************/ diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index 01cedbb200edd..b5d4ab5dc9311 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -2787,9 +2787,11 @@ public sealed partial class XslCompiledTransform public XslCompiledTransform() { } public XslCompiledTransform(bool enableDebug) { } public System.Xml.XmlWriterSettings? OutputSettings { get { throw null; } } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("This method will call into constructors of the earlyBoundTypes array which cannot be statically analyzed.")] public void Load(System.Reflection.MethodInfo executeMethod, byte[] queryData, System.Type[]? earlyBoundTypes) { } public void Load(string stylesheetUri) { } public void Load(string stylesheetUri, System.Xml.Xsl.XsltSettings? settings, System.Xml.XmlResolver? stylesheetResolver) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("This method will get fields and types from the assembly of the passed in compiledStylesheet and call their constructors which cannot be statically analyzed")] public void Load(System.Type compiledStylesheet) { } public void Load(System.Xml.XmlReader stylesheet) { } public void Load(System.Xml.XmlReader stylesheet, System.Xml.Xsl.XsltSettings? settings, System.Xml.XmlResolver? stylesheetResolver) { }