From 412f022a61a9342ee556d13d4271c487b28a617f Mon Sep 17 00:00:00 2001 From: David Cantu Date: Thu, 30 Jul 2020 13:12:54 -0700 Subject: [PATCH] Add nullability annotations to XPath*files in Xml/Cache (#40114) * Add nullability annotations to XPath* files in Xml/Cache * Move missplaced assertion on CachedTextNode * Add product fixes suggested on PR feedback --- .../src/System/Xml/Cache/XPathDocumentBuilder.cs | 104 ++++++++++++-------- .../src/System/Xml/Cache/XPathDocumentNavigator.cs | 43 ++++---- .../src/System/Xml/Cache/XPathNode.cs | 33 ++++--- .../src/System/Xml/Cache/XPathNodeHelper.cs | 108 +++++++++++---------- .../src/System/Xml/Cache/XPathNodeInfoAtom.cs | 95 +++++++++--------- 5 files changed, 210 insertions(+), 173 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentBuilder.cs index ff09876..34c4fcc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentBuilder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable using System; using System.Globalization; using System.IO; @@ -11,6 +12,7 @@ using System.Xml.Schema; using System.Diagnostics; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace MS.Internal.Xml.Cache { @@ -40,21 +42,21 @@ namespace MS.Internal.Xml.Cache private readonly Stack _stkNmsp; // In-scope namespaces private XPathNodeInfoTable _infoTable; // Atomization table for shared node information private XPathDocument _doc; // Currently building document - private IXmlLineInfo _lineInfo; // Line information provider + private IXmlLineInfo? _lineInfo; // Line information provider private XmlNameTable _nameTable; // Atomization table for all names in the document private bool _atomizeNames; // True if all names should be atomized (false if they are pre-atomized) private XPathNode[] _pageNmsp; // Page of last in-scope namespace node private int _idxNmsp; // Page index of last in-scope namespace node - private XPathNode[] _pageParent; // Page of last parent-type node (Element or Root) + private XPathNode[]? _pageParent; // Page of last parent-type node (Element or Root) private int _idxParent; // Page index of last parent-type node (Element or Root) - private XPathNode[] _pageSibling; // Page of previous sibling node (may be null if no previous sibling) + private XPathNode[]? _pageSibling; // Page of previous sibling node (may be null if no previous sibling) private int _idxSibling; // Page index of previous sibling node private int _lineNumBase; // Line number from which offsets are computed private int _linePosBase; // Line position from which offsets are computed - private XmlQualifiedName _idAttrName; // Cached name of an ID attribute - private Hashtable _elemIdMap; // Map from element name to ID attribute name + private XmlQualifiedName? _idAttrName; // Cached name of an ID attribute + private Hashtable? _elemIdMap; // Map from element name to ID attribute name private XPathNodeRef[] _elemNameIndex; // Elements with the same name are linked together so that they can be searched quickly private const int ElementIndexSize = 64; @@ -62,7 +64,7 @@ namespace MS.Internal.Xml.Cache /// /// Create a new XPathDocumentBuilder which creates nodes in "doc". /// - public XPathDocumentBuilder(XPathDocument doc, IXmlLineInfo lineInfo, string baseUri, XPathDocument.LoadFlags flags) + public XPathDocumentBuilder(XPathDocument doc, IXmlLineInfo? lineInfo, string? baseUri, XPathDocument.LoadFlags flags) { // Allocate the initial node (for non-namespaces) page, and the initial namespace page _nodePageFact.Init(256); @@ -77,7 +79,12 @@ namespace MS.Internal.Xml.Cache /// Start construction of a new document. This must be called before any other methods are called. /// It may also be called after Close(), in order to build further documents. /// - public void Initialize(XPathDocument doc, IXmlLineInfo lineInfo, string baseUri, XPathDocument.LoadFlags flags) + [MemberNotNull(nameof(_doc))] + [MemberNotNull(nameof(_elemNameIndex))] + [MemberNotNull(nameof(_infoTable))] + [MemberNotNull(nameof(_nameTable))] + [MemberNotNull(nameof(_pageNmsp))] + public void Initialize(XPathDocument doc, IXmlLineInfo? lineInfo, string? baseUri, XPathDocument.LoadFlags flags) { XPathNode[] page; int idx; @@ -126,14 +133,14 @@ namespace MS.Internal.Xml.Cache /// /// XPathDocument ignores the DocType information. /// - public override void WriteDocType(string name, string pubid, string sysid, string subset) + public override void WriteDocType(string name, string? pubid, string? sysid, string? subset) { } /// /// Shortcut for calling WriteStartElement with elemType == null. /// - public override void WriteStartElement(string prefix, string localName, string ns) + public override void WriteStartElement(string? prefix, string localName, string? ns) { this.WriteStartElement(prefix, localName, ns, string.Empty); } @@ -141,7 +148,7 @@ namespace MS.Internal.Xml.Cache /// /// Build an element node and attach it to its parent, if one exists. Make the element the new parent node. /// - public void WriteStartElement(string prefix, string localName, string ns, string baseUri) + public void WriteStartElement(string? prefix, string localName, string? ns, string? baseUri) { int hash; Debug.Assert(prefix != null && localName != null && ns != null && localName.Length != 0 && baseUri != null); @@ -164,7 +171,7 @@ namespace MS.Internal.Xml.Cache // If elements within this document might have IDs, then cache the name of the ID attribute, if one exists if (_elemIdMap != null) - _idAttrName = (XmlQualifiedName)_elemIdMap[new XmlQualifiedName(localName, prefix)]; + _idAttrName = (XmlQualifiedName?)_elemIdMap[new XmlQualifiedName(localName, prefix)]; } /// @@ -205,7 +212,7 @@ namespace MS.Internal.Xml.Cache public void WriteEndElement(bool allowShortcutTag) { XPathNodeRef nodeRef; - Debug.Assert(_pageParent[_idxParent].NodeType == XPathNodeType.Element); + Debug.Assert(_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element); // If element has no content-typed children except for the one about to be added, then // its value is the same as its only text child's. @@ -277,11 +284,12 @@ namespace MS.Internal.Xml.Cache /// /// Shortcut for calling WriteStartAttribute with attrfType == null. /// - public override void WriteStartAttribute(string prefix, string localName, string namespaceName) + public override void WriteStartAttribute(string? prefix, string localName, string? namespaceName) { - Debug.Assert(!prefix.Equals("xmlns")); - Debug.Assert(_idxParent == 0 || _pageParent[_idxParent].NodeType == XPathNodeType.Element); - Debug.Assert(_idxSibling == 0 || _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute); + Debug.Assert(namespaceName != null); + Debug.Assert(prefix != null && !prefix.Equals("xmlns")); + Debug.Assert(_idxParent == 0 || (_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element)); + Debug.Assert(_idxSibling == 0 || (_pageSibling != null && _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute)); if (_atomizeNames) { @@ -298,6 +306,7 @@ namespace MS.Internal.Xml.Cache /// public override void WriteEndAttribute() { + Debug.Assert(_pageSibling != null); Debug.Assert(_pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute); _pageSibling[_idxSibling].SetValue(_textBldr.ReadText()); @@ -309,8 +318,10 @@ namespace MS.Internal.Xml.Cache _pageSibling[_idxSibling].Prefix == _idAttrName.Namespace) { // Then add its value to the idValueMap map - Debug.Assert(_idxParent != 0, "ID attribute must have an element parent"); - _doc.AddIdElement(_pageSibling[_idxSibling].Value, _pageParent, _idxParent); + Debug.Assert(_idxParent != 0 && _pageParent != null, "ID attribute must have an element parent"); + string? id = _pageSibling[_idxSibling].Value; + Debug.Assert(id != null); + _doc.AddIdElement(id, _pageParent, _idxParent); } } } @@ -318,7 +329,7 @@ namespace MS.Internal.Xml.Cache /// /// Map CData text into regular text. /// - public override void WriteCData(string text) + public override void WriteCData(string? text) { WriteString(text, TextBlockType.Text); } @@ -326,7 +337,7 @@ namespace MS.Internal.Xml.Cache /// /// Construct comment node. /// - public override void WriteComment(string text) + public override void WriteComment(string? text) { AddSibling(XPathNodeType.Comment, string.Empty, string.Empty, string.Empty, string.Empty); _pageSibling[_idxSibling].SetValue(text); @@ -335,7 +346,7 @@ namespace MS.Internal.Xml.Cache /// /// Shortcut for calling WriteProcessingInstruction with baseUri = string.Empty. /// - public override void WriteProcessingInstruction(string name, string text) + public override void WriteProcessingInstruction(string name, string? text) { this.WriteProcessingInstruction(name, text, string.Empty); } @@ -343,7 +354,7 @@ namespace MS.Internal.Xml.Cache /// /// Construct pi node. /// - public void WriteProcessingInstruction(string name, string text, string baseUri) + public void WriteProcessingInstruction(string name, string? text, string? baseUri) { if (_atomizeNames) name = _nameTable.Add(name); @@ -355,7 +366,7 @@ namespace MS.Internal.Xml.Cache /// /// Write a whitespace text block. /// - public override void WriteWhitespace(string ws) + public override void WriteWhitespace(string? ws) { WriteString(ws, TextBlockType.Whitespace); } @@ -363,7 +374,7 @@ namespace MS.Internal.Xml.Cache /// /// Write an attribute or element text block. /// - public override void WriteString(string text) + public override void WriteString(string? text) { WriteString(text, TextBlockType.Text); } @@ -389,7 +400,7 @@ namespace MS.Internal.Xml.Cache /// /// Write an element text block with the specified text type (whitespace, significant whitespace, or text). /// - public void WriteString(string text, TextBlockType textType) + public void WriteString(string? text, TextBlockType textType) { _textBldr.WriteTextBlock(text, textType); } @@ -424,7 +435,7 @@ namespace MS.Internal.Xml.Cache /// public override void Close() { - XPathNode[] page; + XPathNode[]? page; int idx; // If cached text exists, then create a text node at the top-level @@ -470,7 +481,7 @@ namespace MS.Internal.Xml.Cache /// internal override void StartElementContent() { - Debug.Assert(_pageParent[_idxParent].NodeType == XPathNodeType.Element); + Debug.Assert(_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element); } /// @@ -479,12 +490,13 @@ namespace MS.Internal.Xml.Cache /// internal override void WriteNamespaceDeclaration(string prefix, string namespaceName) { - XPathNode[] pageTemp, pageOverride, pageNew, pageOrig, pageCopy; + XPathNode[] pageTemp, pageNew, pageCopy; + XPathNode[]? pageOverride, pageOrig; int idxTemp, idxOverride, idxNew, idxOrig, idxCopy; - Debug.Assert(_idxSibling == 0 || _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute); + Debug.Assert(_idxSibling == 0 || (_pageSibling != null && _pageSibling[_idxSibling].NodeType == XPathNodeType.Attribute)); Debug.Assert(!prefix.Equals("xmlns") && !namespaceName.Equals(XmlReservedNs.NsXmlNs)); Debug.Assert(_idxParent == 0 || _idxNmsp != 0); - Debug.Assert(_idxParent == 0 || _pageParent[_idxParent].NodeType == XPathNodeType.Element); + Debug.Assert(_idxParent == 0 || (_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element)); if (_atomizeNames) prefix = _nameTable.Add(prefix); @@ -496,7 +508,7 @@ namespace MS.Internal.Xml.Cache idxOverride = _idxNmsp; while (idxOverride != 0) { - if ((object)pageOverride[idxOverride].LocalName == (object)prefix) + if ((object)pageOverride![idxOverride].LocalName == (object)prefix) { // Need to clone all namespaces up until the overridden node in order to bypass it break; @@ -517,8 +529,9 @@ namespace MS.Internal.Xml.Cache while (idxOrig != idxOverride || pageOrig != pageOverride) { + Debug.Assert(pageOrig != null); // Make a copy of the original namespace node - idxTemp = pageOrig[idxOrig].GetParent(out pageTemp); + idxTemp = pageOrig[idxOrig].GetParent(out pageTemp!); idxTemp = NewNamespaceNode(out pageTemp, pageOrig[idxOrig].LocalName, pageOrig[idxOrig].Value, pageTemp, idxTemp); // Attach copy to chain of copied nodes @@ -533,10 +546,10 @@ namespace MS.Internal.Xml.Cache } // Link farther up in the original chain, just past the last overridden node - idxOverride = pageOverride[idxOverride].GetSibling(out pageOverride); + idxOverride = pageOverride![idxOverride].GetSibling(out pageOverride); if (idxOverride != 0) - pageCopy[idxCopy].SetSibling(_infoTable, pageOverride, idxOverride); + pageCopy[idxCopy].SetSibling(_infoTable, pageOverride!, idxOverride); else Debug.Assert(prefix.Equals("xml"), "xmlns:xml namespace declaration should always be present in the list."); } @@ -553,6 +566,7 @@ namespace MS.Internal.Xml.Cache if (_idxParent != 0) { + Debug.Assert(_pageParent != null); // If this is the first namespace on the current element, if (!_pageParent[_idxParent].HasNamespaceDecls) { @@ -582,7 +596,7 @@ namespace MS.Internal.Xml.Cache // Extract the elements which has attribute defined as ID from the element declarations foreach (IDtdAttributeListInfo attrList in dtdInfo.GetAttributeLists()) { - IDtdAttributeInfo idAttribute = attrList.LookupIdAttribute(); + IDtdAttributeInfo? idAttribute = attrList.LookupIdAttribute(); if (idAttribute != null) { if (_elemIdMap == null) @@ -612,7 +626,7 @@ namespace MS.Internal.Xml.Cache /// /// Helper method that constructs a new Namespace XPathNode. /// - private int NewNamespaceNode(out XPathNode[] page, string prefix, string namespaceUri, XPathNode[] pageElem, int idxElem) + private int NewNamespaceNode(out XPathNode[] page, string prefix, string? namespaceUri, XPathNode[]? pageElem, int idxElem) { XPathNode[] pageNode; int idxNode, lineNumOffset, linePosOffset; @@ -642,7 +656,7 @@ namespace MS.Internal.Xml.Cache /// /// Helper method that constructs a new XPathNode. /// - private int NewNode(out XPathNode[] page, XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string baseUri) + private int NewNode(out XPathNode[] page, XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string? baseUri) { XPathNode[] pageNode; int idxNode, lineNumOffset, linePosOffset; @@ -714,7 +728,8 @@ namespace MS.Internal.Xml.Cache /// Add a sibling node. If no previous sibling exists, add the node as the first child of the parent. /// If no parent exists, make this node the root of the document. /// - private void AddSibling(XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string baseUri) + [MemberNotNull(nameof(_pageSibling))] + private void AddSibling(XPathNodeType xptyp, string localName, string namespaceUri, string prefix, string? baseUri) { XPathNode[] pageNew; int idxNew; @@ -728,6 +743,7 @@ namespace MS.Internal.Xml.Cache // this.idxParent is only 0 for the top-most node if (_idxParent != 0) { + Debug.Assert(_pageParent != null); // Set properties on parent _pageParent[_idxParent].SetParentProperties(xptyp); @@ -738,6 +754,7 @@ namespace MS.Internal.Xml.Cache } else { + Debug.Assert(_pageSibling != null); // There is already a previous sibling _pageSibling[_idxSibling].SetSibling(_infoTable, pageNew, idxNew); } @@ -750,12 +767,13 @@ namespace MS.Internal.Xml.Cache /// /// Creates a text node from cached text parts. /// + [MemberNotNull(nameof(_pageSibling))] private void CachedTextNode() { TextBlockType textType; string text; Debug.Assert(_textBldr.HasText || (_idxSibling == 0 && _idxParent == 0), "Cannot create empty text node unless it's a top-level text node."); - Debug.Assert(_idxSibling == 0 || !_pageSibling[_idxSibling].IsText, "Cannot create adjacent text nodes."); + Debug.Assert(_idxSibling == 0 || (_pageSibling != null && !_pageSibling[_idxSibling].IsText), "Cannot create adjacent text nodes."); // Create a text node textType = _textBldr.TextType; @@ -833,7 +851,7 @@ namespace MS.Internal.Xml.Cache /// private struct TextBlockBuilder { - private IXmlLineInfo _lineInfo; + private IXmlLineInfo? _lineInfo; private TextBlockType _textType; private string _text; private int _lineNum, _linePos; @@ -841,7 +859,7 @@ namespace MS.Internal.Xml.Cache /// /// Constructor. /// - public void Initialize(IXmlLineInfo lineInfo) + public void Initialize(IXmlLineInfo? lineInfo) { _lineInfo = lineInfo; _textType = TextBlockType.None; @@ -882,12 +900,12 @@ namespace MS.Internal.Xml.Cache /// /// Append a text block with the specified type. /// - public void WriteTextBlock(string text, TextBlockType textType) + public void WriteTextBlock(string? text, TextBlockType textType) { Debug.Assert((int)XPathNodeType.Text < (int)XPathNodeType.SignificantWhitespace); Debug.Assert((int)XPathNodeType.SignificantWhitespace < (int)XPathNodeType.Whitespace); - if (text.Length != 0) + if (!string.IsNullOrEmpty(text)) { if (_textType == TextBlockType.None) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentNavigator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentNavigator.cs index 3ece1a4..18a32e7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentNavigator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentNavigator.cs @@ -63,7 +63,7 @@ namespace MS.Internal.Xml.Cache { get { - string value; + string? value; XPathNode[] page; XPathNode[]? pageEnd; int idx, idxEnd; @@ -94,7 +94,11 @@ namespace MS.Internal.Xml.Cache if (_idxParent != 0) { Debug.Assert(_pageCurrent[_idxCurrent].NodeType == XPathNodeType.Text); - return _pageParent![_idxParent].Value; + + value = _pageParent![_idxParent].Value; + Debug.Assert(value != null); + + return value; } // Must be node with complex content, so concatenate the string values of all text descendants @@ -113,10 +117,12 @@ namespace MS.Internal.Xml.Cache while (XPathNodeHelper.GetTextFollowing(ref page, ref idx, pageEnd, idxEnd)) { Debug.Assert(page[idx].NodeType == XPathNodeType.Element || page[idx].IsText); + value = page[idx].Value; + Debug.Assert(value != null); if (s.Length == 0) { - s = page[idx].Value; + s = value; } else { @@ -125,7 +131,7 @@ namespace MS.Internal.Xml.Cache bldr = new StringBuilder(); bldr.Append(s); } - bldr.Append(page[idx].Value); + bldr.Append(value); } } @@ -193,7 +199,7 @@ namespace MS.Internal.Xml.Cache { get { - XPathNode[] page; + XPathNode[]? page; int idx; if (_idxParent != 0) @@ -210,7 +216,7 @@ namespace MS.Internal.Xml.Cache do { - switch (page[idx].NodeType) + switch (page![idx].NodeType) { case XPathNodeType.Element: case XPathNodeType.Root: @@ -312,7 +318,7 @@ namespace MS.Internal.Xml.Cache /// public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) { - XPathNode[] page; + XPathNode[]? page; int idx; if (namespaceScope == XPathNamespaceScope.Local) @@ -329,11 +335,11 @@ namespace MS.Internal.Xml.Cache while (idx != 0) { // Don't include the xmlns:xml namespace node if scope is ExcludeXml - if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page[idx].IsXmlNamespaceNode) + if (namespaceScope != XPathNamespaceScope.ExcludeXml || !page![idx].IsXmlNamespaceNode) { _pageParent = _pageCurrent; _idxParent = _idxCurrent; - _pageCurrent = page; + _pageCurrent = page!; _idxCurrent = idx; return true; } @@ -351,7 +357,7 @@ namespace MS.Internal.Xml.Cache /// public override bool MoveToNextNamespace(XPathNamespaceScope scope) { - XPathNode[] page = _pageCurrent, pageParent; + XPathNode[]? page = _pageCurrent, pageParent; int idx = _idxCurrent, idxParent; // If current node is not a namespace node, return false @@ -367,12 +373,13 @@ namespace MS.Internal.Xml.Cache if (idx == 0) return false; + Debug.Assert(page != null); switch (scope) { case XPathNamespaceScope.Local: // Once parent changes, there are no longer any local namespaces idxParent = page[idx].GetParent(out pageParent); - if (idxParent != _idxParent || (object)pageParent != (object?)_pageParent) + if (idxParent != _idxParent || (object?)pageParent != (object?)_pageParent) return false; break; @@ -614,7 +621,7 @@ namespace MS.Internal.Xml.Cache // If this navigator is positioned on a virtual node, then compute following of parent if (_idxParent != 0) { - if (!XPathNodeHelper.GetElementFollowing(ref _pageParent, ref _idxParent, pageEnd, idxEnd, _atomizedLocalName, namespaceURI)) + if (!XPathNodeHelper.GetElementFollowing(ref _pageParent!, ref _idxParent, pageEnd, idxEnd, _atomizedLocalName, namespaceURI)) return false; _pageCurrent = _pageParent; @@ -713,7 +720,7 @@ namespace MS.Internal.Xml.Cache // If this navigator is positioned on a virtual node, then compute following of parent if (_idxParent != 0) { - if (!XPathNodeHelper.GetContentFollowing(ref _pageParent, ref _idxParent, pageEnd, idxEnd, type)) + if (!XPathNodeHelper.GetContentFollowing(ref _pageParent!, ref _idxParent, pageEnd, idxEnd, type)) return false; _pageCurrent = _pageParent; @@ -812,7 +819,7 @@ namespace MS.Internal.Xml.Cache XPathDocumentNavigator? that = other as XPathDocumentNavigator; if (that != null) { - XPathNode[] pageThat; + XPathNode[]? pageThat; int idxThat; // If that current node's parent is virtualized, then start with the virtual parent @@ -830,7 +837,7 @@ namespace MS.Internal.Xml.Cache { if (idxThat == _idxCurrent && pageThat == _pageCurrent) return true; - idxThat = pageThat[idxThat].GetParent(out pageThat); + idxThat = pageThat![idxThat].GetParent(out pageThat); } } return false; @@ -852,7 +859,7 @@ namespace MS.Internal.Xml.Cache } // Yes, so primary location should be derived from parent node - return XPathNodeHelper.GetLocation(_pageParent, _idxParent); + return XPathNodeHelper.GetLocation(_pageParent!, _idxParent); } /// @@ -901,7 +908,7 @@ namespace MS.Internal.Xml.Cache // If the current node is virtualized, code its parent if (_idxParent != 0) { - loc = (_pageParent![0].PageInfo.PageNumber - 1) << 16 | (_idxParent - 1); + loc = (_pageParent![0].PageInfo!.PageNumber - 1) << 16 | (_idxParent - 1); do { buf[idx++] = UniqueIdTbl[loc & 0x1f]; @@ -911,7 +918,7 @@ namespace MS.Internal.Xml.Cache } // Code the node itself - loc = (_pageCurrent[0].PageInfo.PageNumber - 1) << 16 | (_idxCurrent - 1); + loc = (_pageCurrent[0].PageInfo!.PageNumber - 1) << 16 | (_idxCurrent - 1); do { buf[idx++] = UniqueIdTbl[loc & 0x1f]; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs index 75ab164..e8c1dd5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable using System; using System.Diagnostics; using System.Xml.XPath; @@ -33,7 +34,7 @@ namespace MS.Internal.Xml.Cache private ushort _idxSimilar; // Page index of next node in document order that has local name with same hashcode private ushort _posOffset; // Line position offset of node (added to LinePositionBase) private uint _props; // Node properties (broken down into bits below) - private string _value; // String value of node + private string? _value; // String value of node private const uint NodeTypeMask = 0xF; private const uint HasAttributeBit = 0x10; @@ -124,7 +125,7 @@ namespace MS.Internal.Xml.Cache /// public string BaseUri { - get { return _info.BaseUri; } + get { return _info.BaseUri ?? string.Empty; } } /// @@ -159,7 +160,7 @@ namespace MS.Internal.Xml.Cache /// /// Returns information about the node page. Only the 0th node on each page has this property defined. /// - public XPathNodePageInfo PageInfo + public XPathNodePageInfo? PageInfo { get { return _info.PageInfo; } } @@ -169,13 +170,15 @@ namespace MS.Internal.Xml.Cache /// public int GetRoot(out XPathNode[] pageNode) { - return _info.Document.GetRootNode(out pageNode); + int idx = _info.Document.GetRootNode(out pageNode!); + Debug.Assert(pageNode != null); + return idx; } /// /// Returns the parent of this node. If this node has no parent, then 0 is returned. /// - public int GetParent(out XPathNode[] pageNode) + public int GetParent(out XPathNode[]? pageNode) { pageNode = _info.ParentPage; return _idxParent; @@ -184,7 +187,7 @@ namespace MS.Internal.Xml.Cache /// /// Returns the next sibling of this node. If this node has no next sibling, then 0 is returned. /// - public int GetSibling(out XPathNode[] pageNode) + public int GetSibling(out XPathNode[]? pageNode) { pageNode = _info.SiblingPage; return _idxSibling; @@ -194,7 +197,7 @@ namespace MS.Internal.Xml.Cache /// Returns the next element in document order that has the same local name hashcode as this element. /// If there are no similar elements, then 0 is returned. /// - public int GetSimilarElement(out XPathNode[] pageNode) + public int GetSimilarElement(out XPathNode[]? pageNode) { pageNode = _info.SimilarElementPage; return _idxSimilar; @@ -204,11 +207,11 @@ namespace MS.Internal.Xml.Cache /// Returns true if this node's name matches the specified localName and namespaceName. Assume /// that localName has been atomized, but namespaceName has not. /// - public bool NameMatch(string localName, string namespaceName) + public bool NameMatch(string? localName, string namespaceName) { - Debug.Assert(localName == null || (object)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized."); + Debug.Assert(localName == null || (object?)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized."); - return (object)_info.LocalName == (object)localName && + return (object)_info.LocalName == (object?)localName && _info.NamespaceUri == namespaceName; } @@ -216,12 +219,12 @@ namespace MS.Internal.Xml.Cache /// Returns true if this is an Element node with a name that matches the specified localName and /// namespaceName. Assume that localName has been atomized, but namespaceName has not. /// - public bool ElementMatch(string localName, string namespaceName) + public bool ElementMatch(string? localName, string namespaceName) { - Debug.Assert(localName == null || (object)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized."); + Debug.Assert(localName == null || (object?)Document.NameTable.Get(localName) == (object)localName, "localName must be atomized."); return NodeType == XPathNodeType.Element && - (object)_info.LocalName == (object)localName && + (object)_info.LocalName == (object?)localName && _info.NamespaceUri == namespaceName; } @@ -333,7 +336,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the precomputed String value of this node (null if no value exists, i.e. document node, element node with complex content, etc). /// - public string Value + public string? Value { get { return _value; } } @@ -385,7 +388,7 @@ namespace MS.Internal.Xml.Cache /// /// Set this node's value. /// - public void SetValue(string value) + public void SetValue(string? value) { _value = value; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeHelper.cs index dd47732..aaea5e0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeHelper.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable using System; using System.Diagnostics; using System.Xml.XPath; @@ -18,7 +19,7 @@ namespace MS.Internal.Xml.Cache /// parent is this node). Subsequent nodes may not have the same node as parent, so the caller will /// need to test the parent in order to terminate a search that processes only local namespaces. /// - public static int GetLocalNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[] pageNmsp) + public static int GetLocalNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[]? pageNmsp) { if (pageElem[idxElem].HasNamespaceDecls) { @@ -35,7 +36,7 @@ namespace MS.Internal.Xml.Cache /// have this element as their parent. Since the xmlns:xml namespace node is always in scope, this /// method will never return 0 if the specified node is an element. /// - public static int GetInScopeNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[] pageNmsp) + public static int GetInScopeNamespaces(XPathNode[] pageElem, int idxElem, out XPathNode[]? pageNmsp) { XPathDocument doc; @@ -47,7 +48,7 @@ namespace MS.Internal.Xml.Cache // Walk ancestors, looking for an ancestor that has at least one namespace declaration while (!pageElem[idxElem].HasNamespaceDecls) { - idxElem = pageElem[idxElem].GetParent(out pageElem); + idxElem = pageElem[idxElem].GetParent(out pageElem!); if (idxElem == 0) { // There are no namespace nodes declared on ancestors, so return xmlns:xml node @@ -84,12 +85,12 @@ namespace MS.Internal.Xml.Cache /// public static bool GetNextAttribute(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] page; + XPathNode[]? page; int idx; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); idx = pageNode[idxNode].GetSibling(out page); - if (idx != 0 && page[idx].NodeType == XPathNodeType.Attribute) + if (idx != 0 && page![idx].NodeType == XPathNodeType.Attribute) { pageNode = page; idxNode = idx; @@ -104,7 +105,7 @@ namespace MS.Internal.Xml.Cache /// public static bool GetContentChild(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -117,6 +118,7 @@ namespace MS.Internal.Xml.Cache { idx = page[idx].GetSibling(out page); Debug.Assert(idx != 0); + Debug.Assert(page != null); } pageNode = page; @@ -132,7 +134,7 @@ namespace MS.Internal.Xml.Cache /// public static bool GetContentSibling(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -141,7 +143,7 @@ namespace MS.Internal.Xml.Cache idx = page[idx].GetSibling(out page); if (idx != 0) { - pageNode = page; + pageNode = page!; idxNode = idx; return true; } @@ -155,14 +157,14 @@ namespace MS.Internal.Xml.Cache /// public static bool GetParent(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); idx = page[idx].GetParent(out page); if (idx != 0) { - pageNode = page; + pageNode = page!; idxNode = idx; return true; } @@ -177,8 +179,8 @@ namespace MS.Internal.Xml.Cache { Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); Debug.Assert(idxNode <= ushort.MaxValue); - Debug.Assert(pageNode[0].PageInfo.PageNumber <= short.MaxValue); - return (pageNode[0].PageInfo.PageNumber << 16) | idxNode; + Debug.Assert(pageNode[0].PageInfo!.PageNumber <= short.MaxValue); + return (pageNode[0].PageInfo!.PageNumber << 16) | idxNode; } /// @@ -186,9 +188,9 @@ namespace MS.Internal.Xml.Cache /// then do not set pageNode or idxNode and return false. Assume that the localName has been atomized with respect /// to this document's name table, but not the namespaceName. /// - public static bool GetElementChild(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName) + public static bool GetElementChild(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -201,7 +203,7 @@ namespace MS.Internal.Xml.Cache // Find element with specified localName and namespaceName do { - if (page[idx].ElementMatch(localName, namespaceName)) + if (page![idx].ElementMatch(localName, namespaceName)) { pageNode = page; idxNode = idx; @@ -220,9 +222,9 @@ namespace MS.Internal.Xml.Cache /// return false. Assume that the localName has been atomized with respect to this document's name table, /// but not the namespaceName. /// - public static bool GetElementSibling(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName) + public static bool GetElementSibling(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -236,7 +238,7 @@ namespace MS.Internal.Xml.Cache if (idx == 0) break; - if (page[idx].ElementMatch(localName, namespaceName)) + if (page![idx].ElementMatch(localName, namespaceName)) { pageNode = page; idxNode = idx; @@ -254,7 +256,7 @@ namespace MS.Internal.Xml.Cache /// public static bool GetContentChild(ref XPathNode[] pageNode, ref int idxNode, XPathNodeType typ) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; int mask; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -267,7 +269,7 @@ namespace MS.Internal.Xml.Cache GetChild(ref page, ref idx); do { - if (((1 << (int)page[idx].NodeType) & mask) != 0) + if (((1 << (int)page![idx].NodeType) & mask) != 0) { // Never return attributes, as Attribute is not a content type if (typ == XPathNodeType.Attribute) @@ -292,7 +294,7 @@ namespace MS.Internal.Xml.Cache /// public static bool GetContentSibling(ref XPathNode[] pageNode, ref int idxNode, XPathNodeType typ) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; int mask = XPathNavigator.GetContentKindMask(typ); Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -306,7 +308,7 @@ namespace MS.Internal.Xml.Cache if (idx == 0) break; - if (((1 << (int)page[idx].NodeType) & mask) != 0) + if (((1 << (int)page![idx].NodeType) & mask) != 0) { Debug.Assert(typ != XPathNodeType.Attribute && typ != XPathNodeType.Namespace); pageNode = page; @@ -325,7 +327,7 @@ namespace MS.Internal.Xml.Cache /// public static bool GetPreviousContentSibling(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] pageParent = pageNode, pagePrec, pageAnc; + XPathNode[]? pageParent, pagePrec, pageAnc; int idxParent = idxNode, idxPrec, idxAnc; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); Debug.Assert(pageNode[idxNode].NodeType != XPathNodeType.Attribute); @@ -336,14 +338,15 @@ namespace MS.Internal.Xml.Cache // 3. Get node that immediately precedes the current node in document order // 4. If preceding node is parent, then there is no previous sibling, so return false // 5. Walk ancestors of preceding node, until parent of current node is found - idxParent = pageParent[idxParent].GetParent(out pageParent); + idxParent = pageNode[idxParent].GetParent(out pageParent); if (idxParent != 0) { idxPrec = idxNode - 1; if (idxPrec == 0) { // Need to get previous page - pagePrec = pageNode[0].PageInfo.PreviousPage; + pagePrec = pageNode[0].PageInfo!.PreviousPage; + Debug.Assert(pagePrec != null); idxPrec = pagePrec.Length - 1; } else @@ -385,9 +388,9 @@ namespace MS.Internal.Xml.Cache /// then do not set pageNode or idxNode and return false. Assume that the localName has been atomized with respect /// to this document's name table, but not the namespaceName. /// - public static bool GetAttribute(ref XPathNode[] pageNode, ref int idxNode, string localName, string namespaceName) + public static bool GetAttribute(ref XPathNode[] pageNode, ref int idxNode, string? localName, string namespaceName) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; Debug.Assert(pageNode != null && idxNode != 0, "Cannot pass null argument(s)"); @@ -405,7 +408,7 @@ namespace MS.Internal.Xml.Cache } idx = page[idx].GetSibling(out page); } - while (idx != 0 && page[idx].NodeType == XPathNodeType.Attribute); + while (idx != 0 && page![idx].NodeType == XPathNodeType.Attribute); } return false; @@ -419,14 +422,14 @@ namespace MS.Internal.Xml.Cache /// If no such element exists, then do not set pageCurrent or idxCurrent and return false. /// Assume that the localName has been atomized with respect to this document's name table, but not the namespaceName. /// - public static bool GetElementFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd, string localName, string namespaceName) + public static bool GetElementFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd, string? localName, string namespaceName) { - XPathNode[] page = pageCurrent; + XPathNode[]? page = pageCurrent; int idx = idxCurrent; Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)"); // If current node is an element having a matching name, - if (page[idx].NodeType == XPathNodeType.Element && (object)page[idx].LocalName == (object)localName) + if (page[idx].NodeType == XPathNodeType.Element && (object)page[idx].LocalName == (object?)localName) { // Then follow similar element name pointers int idxPageEnd = 0; @@ -434,8 +437,8 @@ namespace MS.Internal.Xml.Cache if (pageEnd != null) { - idxPageEnd = pageEnd[0].PageInfo.PageNumber; - idxPageCurrent = page[0].PageInfo.PageNumber; + idxPageEnd = pageEnd[0].PageInfo!.PageNumber; + idxPageCurrent = page[0].PageInfo!.PageNumber; // If ending node is <= starting node in document order, then scan to end of document if (idxPageCurrent > idxPageEnd || (idxPageCurrent == idxPageEnd && idx >= idxEnd)) @@ -449,10 +452,12 @@ namespace MS.Internal.Xml.Cache if (idx == 0) break; + Debug.Assert(page != null); + // Only scan to ending node if (pageEnd != null) { - idxPageCurrent = page[0].PageInfo.PageNumber; + idxPageCurrent = page[0].PageInfo!.PageNumber; if (idxPageCurrent > idxPageEnd) break; @@ -472,7 +477,7 @@ namespace MS.Internal.Xml.Cache idx++; do { - if ((object)page == (object)pageEnd && idx <= idxEnd) + if ((object)page == (object?)pageEnd && idx <= idxEnd) { // Only scan to termination point while (idx != idxEnd) @@ -486,7 +491,7 @@ namespace MS.Internal.Xml.Cache else { // Scan all nodes in the page - while (idx < page[0].PageInfo.NodeCount) + while (idx < page[0].PageInfo!.NodeCount) { if (page[idx].ElementMatch(localName, namespaceName)) goto FoundNode; @@ -494,7 +499,7 @@ namespace MS.Internal.Xml.Cache } } - page = page[0].PageInfo.NextPage; + page = page[0].PageInfo!.NextPage; idx = 1; } while (page != null); @@ -515,9 +520,9 @@ namespace MS.Internal.Xml.Cache /// 3. Has the specified XPathNodeType (but Attributes and Namespaces never match) /// If no such node exists, then do not set pageCurrent or idxCurrent and return false. /// - public static bool GetContentFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd, XPathNodeType typ) + public static bool GetContentFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd, XPathNodeType typ) { - XPathNode[] page = pageCurrent; + XPathNode[]? page = pageCurrent; int idx = idxCurrent; int mask = XPathNavigator.GetContentKindMask(typ); Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)"); @@ -529,7 +534,7 @@ namespace MS.Internal.Xml.Cache idx++; do { - if ((object)page == (object)pageEnd && idx <= idxEnd) + if ((object)page == (object?)pageEnd && idx <= idxEnd) { // Only scan to termination point while (idx != idxEnd) @@ -543,7 +548,7 @@ namespace MS.Internal.Xml.Cache else { // Scan all nodes in the page - while (idx < page[0].PageInfo.NodeCount) + while (idx < page[0].PageInfo!.NodeCount) { if (((1 << (int)page[idx].NodeType) & mask) != 0) goto FoundNode; @@ -551,7 +556,7 @@ namespace MS.Internal.Xml.Cache } } - page = page[0].PageInfo.NextPage; + page = page[0].PageInfo!.NextPage; idx = 1; } while (page != null); @@ -574,9 +579,9 @@ namespace MS.Internal.Xml.Cache /// 2. Non-collapsed text nodes /// If no such node exists, then do not set pageCurrent or idxCurrent and return false. /// - public static bool GetTextFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[] pageEnd, int idxEnd) + public static bool GetTextFollowing(ref XPathNode[] pageCurrent, ref int idxCurrent, XPathNode[]? pageEnd, int idxEnd) { - XPathNode[] page = pageCurrent; + XPathNode[]? page = pageCurrent; int idx = idxCurrent; Debug.Assert(pageCurrent != null && idxCurrent != 0, "Cannot pass null argument(s)"); Debug.Assert(!page[idx].IsAttrNmsp, "Current node should never be an attribute or namespace--caller should handle this case."); @@ -586,7 +591,7 @@ namespace MS.Internal.Xml.Cache idx++; do { - if ((object)page == (object)pageEnd && idx <= idxEnd) + if ((object)page == (object?)pageEnd && idx <= idxEnd) { // Only scan to termination point while (idx != idxEnd) @@ -600,7 +605,7 @@ namespace MS.Internal.Xml.Cache else { // Scan all nodes in the page - while (idx < page[0].PageInfo.NodeCount) + while (idx < page[0].PageInfo!.NodeCount) { if (page[idx].IsText || (page[idx].NodeType == XPathNodeType.Element && page[idx].HasCollapsedText)) goto FoundNode; @@ -608,7 +613,7 @@ namespace MS.Internal.Xml.Cache } } - page = page[0].PageInfo.NextPage; + page = page[0].PageInfo!.NextPage; idx = 1; } while (page != null); @@ -628,18 +633,18 @@ namespace MS.Internal.Xml.Cache /// public static bool GetNonDescendant(ref XPathNode[] pageNode, ref int idxNode) { - XPathNode[] page = pageNode; + XPathNode[]? page = pageNode; int idx = idxNode; // Get page, idx at which to end sequential scan of nodes do { // If the current node has a sibling, - if (page[idx].HasSibling) + if (page![idx].HasSibling) { // Then that is the first non-descendant pageNode = page; - idxNode = page[idx].GetSibling(out pageNode); + idxNode = page[idx].GetSibling(out pageNode!); return true; } @@ -662,7 +667,8 @@ namespace MS.Internal.Xml.Cache if (++idxNode >= pageNode.Length) { // Child is first node on next page - pageNode = pageNode[0].PageInfo.NextPage; + pageNode = pageNode[0].PageInfo!.NextPage!; + Debug.Assert(pageNode != null); idxNode = 1; } // Else child is next node on this page diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs index 0018440..4396a83 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable using System; using System.Diagnostics; using System.Text; @@ -17,13 +18,13 @@ namespace MS.Internal.Xml.Cache { private readonly int _pageNum; private int _nodeCount; - private readonly XPathNode[] _pagePrev; - private XPathNode[] _pageNext; + private readonly XPathNode[]? _pagePrev; + private XPathNode[]? _pageNext; /// /// Constructor. /// - public XPathNodePageInfo(XPathNode[] pagePrev, int pageNum) + public XPathNodePageInfo(XPathNode[]? pagePrev, int pageNum) { _pagePrev = pagePrev; _pageNum = pageNum; @@ -50,7 +51,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the previous node page in the document. /// - public XPathNode[] PreviousPage + public XPathNode[]? PreviousPage { get { return _pagePrev; } } @@ -58,7 +59,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the next node page in the document. /// - public XPathNode[] NextPage + public XPathNode[]? NextPage { get { return _pageNext; } set { _pageNext = value; } @@ -77,20 +78,20 @@ namespace MS.Internal.Xml.Cache /// internal sealed class XPathNodeInfoAtom : IEquatable { - private string _localName; - private string _namespaceUri; - private string _prefix; - private string _baseUri; - private XPathNode[] _pageParent; - private XPathNode[] _pageSibling; - private XPathNode[] _pageSimilar; - private XPathDocument _doc; + private string _localName = null!; + private string _namespaceUri = null!; + private string _prefix = null!; + private string? _baseUri; + private XPathNode[]? _pageParent; + private XPathNode[]? _pageSibling; + private XPathNode[]? _pageSimilar; + private XPathDocument _doc = null!; private int _lineNumBase; private int _linePosBase; private int _hashCode; private int _localNameHash; - private XPathNodeInfoAtom _next; - private XPathNodePageInfo _pageInfo; + private XPathNodeInfoAtom? _next; + private XPathNodePageInfo? _pageInfo; /// @@ -105,8 +106,8 @@ namespace MS.Internal.Xml.Cache /// /// Construct a new shared information atom. This method should only be used by the XNodeInfoTable. /// - public XPathNodeInfoAtom(string localName, string namespaceUri, string prefix, string baseUri, - XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar, + public XPathNodeInfoAtom(string localName, string namespaceUri, string prefix, string? baseUri, + XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar, XPathDocument doc, int lineNumBase, int linePosBase) { Init(localName, namespaceUri, prefix, baseUri, pageParent, pageSibling, pageSimilar, doc, lineNumBase, linePosBase); @@ -115,8 +116,8 @@ namespace MS.Internal.Xml.Cache /// /// Initialize an existing shared information atom. This method should only be used by the XNodeInfoTable. /// - public void Init(string localName, string namespaceUri, string prefix, string baseUri, - XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar, + public void Init(string localName, string namespaceUri, string prefix, string? baseUri, + XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar, XPathDocument doc, int lineNumBase, int linePosBase) { Debug.Assert(localName != null && namespaceUri != null && prefix != null && doc != null); @@ -143,7 +144,7 @@ namespace MS.Internal.Xml.Cache /// /// Returns information about the node page. Only the 0th node on each page has this property defined. /// - public XPathNodePageInfo PageInfo + public XPathNodePageInfo? PageInfo { get { return _pageInfo; } } @@ -175,7 +176,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the base Uri of nodes that share this information atom. /// - public string BaseUri + public string? BaseUri { get { return _baseUri; } } @@ -183,7 +184,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the page containing the next sibling of nodes that share this information atom. /// - public XPathNode[] SiblingPage + public XPathNode[]? SiblingPage { get { return _pageSibling; } } @@ -191,7 +192,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the page containing the next element having a name which has same hashcode as this element. /// - public XPathNode[] SimilarElementPage + public XPathNode[]? SimilarElementPage { get { return _pageSimilar; } } @@ -199,7 +200,7 @@ namespace MS.Internal.Xml.Cache /// /// Return the page containing the parent of nodes that share this information atom. /// - public XPathNode[] ParentPage + public XPathNode[]? ParentPage { get { return _pageParent; } } @@ -239,7 +240,7 @@ namespace MS.Internal.Xml.Cache /// /// Link together InfoAtoms that hash to the same hashtable bucket (should only be used by XPathNodeInfoTable) /// - public XPathNodeInfoAtom Next + public XPathNodeInfoAtom? Next { get { return _next; } set { _next = value; } @@ -261,13 +262,13 @@ namespace MS.Internal.Xml.Cache unchecked { if (_pageSibling != null) - hashCode += (hashCode << 7) ^ _pageSibling[0].PageInfo.PageNumber; + hashCode += (hashCode << 7) ^ _pageSibling[0].PageInfo!.PageNumber; if (_pageParent != null) - hashCode += (hashCode << 7) ^ _pageParent[0].PageInfo.PageNumber; + hashCode += (hashCode << 7) ^ _pageParent[0].PageInfo!.PageNumber; if (_pageSimilar != null) - hashCode += (hashCode << 7) ^ _pageSimilar[0].PageInfo.PageNumber; + hashCode += (hashCode << 7) ^ _pageSimilar[0].PageInfo!.PageNumber; } // Save hashcode. Don't save 0, so that it won't ever be recomputed. @@ -280,27 +281,27 @@ namespace MS.Internal.Xml.Cache /// /// Return true if this InfoAtom has the same values as another InfoAtom. /// - public override bool Equals(object other) + public override bool Equals(object? other) { return Equals(other as XPathNodeInfoAtom); } - public bool Equals(XPathNodeInfoAtom other) + public bool Equals(XPathNodeInfoAtom? other) { Debug.Assert(other != null); - Debug.Assert((object)_doc == (object)other._doc); + Debug.Assert((object?)_doc == (object?)other._doc); Debug.Assert(_pageInfo == null); // Assume that name parts are atomized if (this.GetHashCode() == other.GetHashCode()) { - if ((object)_localName == (object)other._localName && - (object)_pageSibling == (object)other._pageSibling && - (object)_namespaceUri == (object)other._namespaceUri && - (object)_pageParent == (object)other._pageParent && - (object)_pageSimilar == (object)other._pageSimilar && - (object)_prefix == (object)other._prefix && - (object)_baseUri == (object)other._baseUri && + if ((object?)_localName == (object?)other._localName && + (object?)_pageSibling == (object?)other._pageSibling && + (object?)_namespaceUri == (object?)other._namespaceUri && + (object?)_pageParent == (object?)other._pageParent && + (object?)_pageSimilar == (object?)other._pageSimilar && + (object?)_prefix == (object?)other._prefix && + (object?)_baseUri == (object?)other._baseUri && _lineNumBase == other._lineNumBase && _linePosBase == other._linePosBase) { @@ -322,12 +323,14 @@ namespace MS.Internal.Xml.Cache bldr.Append(GetHashCode()); bldr.Append(", "); + Debug.Assert(_localName != null); if (_localName.Length != 0) { bldr.Append('{'); bldr.Append(_namespaceUri); bldr.Append('}'); + Debug.Assert(_prefix != null); if (_prefix.Length != 0) { bldr.Append(_prefix); @@ -341,21 +344,21 @@ namespace MS.Internal.Xml.Cache if (_pageParent != null) { bldr.Append("parent="); - bldr.Append(_pageParent[0].PageInfo.PageNumber); + bldr.Append(_pageParent[0].PageInfo!.PageNumber); bldr.Append(", "); } if (_pageSibling != null) { bldr.Append("sibling="); - bldr.Append(_pageSibling[0].PageInfo.PageNumber); + bldr.Append(_pageSibling[0].PageInfo!.PageNumber); bldr.Append(", "); } if (_pageSimilar != null) { bldr.Append("similar="); - bldr.Append(_pageSimilar[0].PageInfo.PageNumber); + bldr.Append(_pageSimilar[0].PageInfo!.PageNumber); bldr.Append(", "); } @@ -378,7 +381,7 @@ namespace MS.Internal.Xml.Cache { private XPathNodeInfoAtom[] _hashTable; private int _sizeTable; - private XPathNodeInfoAtom _infoCached; + private XPathNodeInfoAtom? _infoCached; #if DEBUG private const int DefaultTableSize = 2; @@ -398,8 +401,8 @@ namespace MS.Internal.Xml.Cache /// /// Create a new XNodeInfoAtom and ensure it is atomized in the table. /// - public XPathNodeInfoAtom Create(string localName, string namespaceUri, string prefix, string baseUri, - XPathNode[] pageParent, XPathNode[] pageSibling, XPathNode[] pageSimilar, + public XPathNodeInfoAtom Create(string localName, string namespaceUri, string prefix, string? baseUri, + XPathNode[]? pageParent, XPathNode[]? pageSibling, XPathNode[]? pageSimilar, XPathDocument doc, int lineNumBase, int linePosBase) { XPathNodeInfoAtom info; @@ -434,7 +437,7 @@ namespace MS.Internal.Xml.Cache /// private XPathNodeInfoAtom Atomize(XPathNodeInfoAtom info) { - XPathNodeInfoAtom infoNew, infoNext; + XPathNodeInfoAtom? infoNew, infoNext; // Search for existing XNodeInfoAtom in the table infoNew = _hashTable[info.GetHashCode() & (_hashTable.Length - 1)]; @@ -492,7 +495,7 @@ namespace MS.Internal.Xml.Cache public override string ToString() { StringBuilder bldr = new StringBuilder(); - XPathNodeInfoAtom infoAtom; + XPathNodeInfoAtom? infoAtom; for (int i = 0; i < _hashTable.Length; i++) { -- 2.7.4