Add nullability annotations to XPath*files in Xml/Cache (#40114)
authorDavid Cantu <dacantu@microsoft.com>
Thu, 30 Jul 2020 20:12:54 +0000 (13:12 -0700)
committerGitHub <noreply@github.com>
Thu, 30 Jul 2020 20:12:54 +0000 (13:12 -0700)
* Add nullability annotations to XPath* files in Xml/Cache

* Move missplaced assertion on CachedTextNode

* Add product fixes suggested on PR feedback

src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentBuilder.cs
src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentNavigator.cs
src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs
src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeHelper.cs
src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs

index ff09876..34c4fcc 100644 (file)
@@ -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<XPathNodeRef> _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
         /// <summary>
         /// Create a new XPathDocumentBuilder which creates nodes in "doc".
         /// </summary>
-        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.
         /// </summary>
-        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
         /// <summary>
         /// XPathDocument ignores the DocType information.
         /// </summary>
-        public override void WriteDocType(string name, string pubid, string sysid, string subset)
+        public override void WriteDocType(string name, string? pubid, string? sysid, string? subset)
         {
         }
 
         /// <summary>
         /// Shortcut for calling WriteStartElement with elemType == null.
         /// </summary>
-        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
         /// <summary>
         /// Build an element node and attach it to its parent, if one exists.  Make the element the new parent node.
         /// </summary>
-        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)];
         }
 
         /// <summary>
@@ -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
         /// <summary>
         /// Shortcut for calling WriteStartAttribute with attrfType == null.
         /// </summary>
-        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
         /// </summary>
         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
         /// <summary>
         /// Map CData text into regular text.
         /// </summary>
-        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
         /// <summary>
         /// Construct comment node.
         /// </summary>
-        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
         /// <summary>
         /// Shortcut for calling WriteProcessingInstruction with baseUri = string.Empty.
         /// </summary>
-        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
         /// <summary>
         /// Construct pi node.
         /// </summary>
-        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
         /// <summary>
         /// Write a whitespace text block.
         /// </summary>
-        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
         /// <summary>
         /// Write an attribute or element text block.
         /// </summary>
-        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
         /// <summary>
         /// Write an element text block with the specified text type (whitespace, significant whitespace, or text).
         /// </summary>
-        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
         /// </summary>
         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
         /// </summary>
         internal override void StartElementContent()
         {
-            Debug.Assert(_pageParent[_idxParent].NodeType == XPathNodeType.Element);
+            Debug.Assert(_pageParent != null && _pageParent[_idxParent].NodeType == XPathNodeType.Element);
         }
 
         /// <summary>
@@ -479,12 +490,13 @@ namespace MS.Internal.Xml.Cache
         /// </summary>
         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
         /// <summary>
         /// Helper method that constructs a new Namespace XPathNode.
         /// </summary>
-        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
         /// <summary>
         /// Helper method that constructs a new XPathNode.
         /// </summary>
-        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.
         /// </summary>
-        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
         /// <summary>
         /// Creates a text node from cached text parts.
         /// </summary>
+        [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
         /// </summary>
         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
             /// <summary>
             /// Constructor.
             /// </summary>
-            public void Initialize(IXmlLineInfo lineInfo)
+            public void Initialize(IXmlLineInfo? lineInfo)
             {
                 _lineInfo = lineInfo;
                 _textType = TextBlockType.None;
@@ -882,12 +900,12 @@ namespace MS.Internal.Xml.Cache
             /// <summary>
             /// Append a text block with the specified type.
             /// </summary>
-            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)
                     {
index 3ece1a4..18a32e7 100644 (file)
@@ -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
         /// </summary>
         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
         /// </summary>
         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);
         }
 
         /// <summary>
@@ -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];
index 75ab164..e8c1dd5 100644 (file)
@@ -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
         /// </summary>
         public string BaseUri
         {
-            get { return _info.BaseUri; }
+            get { return _info.BaseUri ?? string.Empty; }
         }
 
         /// <summary>
@@ -159,7 +160,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Returns information about the node page.  Only the 0th node on each page has this property defined.
         /// </summary>
-        public XPathNodePageInfo PageInfo
+        public XPathNodePageInfo? PageInfo
         {
             get { return _info.PageInfo; }
         }
@@ -169,13 +170,15 @@ namespace MS.Internal.Xml.Cache
         /// </summary>
         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;
         }
 
         /// <summary>
         /// Returns the parent of this node.  If this node has no parent, then 0 is returned.
         /// </summary>
-        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
         /// <summary>
         /// Returns the next sibling of this node.  If this node has no next sibling, then 0 is returned.
         /// </summary>
-        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.
         /// </summary>
-        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.
         /// </summary>
-        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.
         /// </summary>
-        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
         /// <summary>
         /// Return the precomputed String value of this node (null if no value exists, i.e. document node, element node with complex content, etc).
         /// </summary>
-        public string Value
+        public string? Value
         {
             get { return _value; }
         }
@@ -385,7 +388,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Set this node's value.
         /// </summary>
-        public void SetValue(string value)
+        public void SetValue(string? value)
         {
             _value = value;
         }
index dd47732..aaea5e0 100644 (file)
@@ -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.
         /// </summary>
-        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.
         /// </summary>
-        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
         /// </summary>
         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
         /// </summary>
         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
         /// </summary>
         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
         /// </summary>
         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;
         }
 
         /// <summary>
@@ -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.
         /// </summary>
-        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.
         /// </summary>
-        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
         /// </summary>
         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
         /// </summary>
         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
         /// </summary>
         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.
         /// </summary>
-        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.
         /// </summary>
-        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.
         /// </summary>
-        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.
         /// </summary>
-        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
         /// </summary>
         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
index 0018440..4396a83 100644 (file)
@@ -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;
 
         /// <summary>
         /// Constructor.
         /// </summary>
-        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
         /// <summary>
         /// Return the previous node page in the document.
         /// </summary>
-        public XPathNode[] PreviousPage
+        public XPathNode[]? PreviousPage
         {
             get { return _pagePrev; }
         }
@@ -58,7 +59,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Return the next node page in the document.
         /// </summary>
-        public XPathNode[] NextPage
+        public XPathNode[]? NextPage
         {
             get { return _pageNext; }
             set { _pageNext = value; }
@@ -77,20 +78,20 @@ namespace MS.Internal.Xml.Cache
     /// </summary>
     internal sealed class XPathNodeInfoAtom : IEquatable<XPathNodeInfoAtom>
     {
-        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;
 
 
         /// <summary>
@@ -105,8 +106,8 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Construct a new shared information atom.  This method should only be used by the XNodeInfoTable.
         /// </summary>
-        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
         /// <summary>
         /// Initialize an existing shared information atom.  This method should only be used by the XNodeInfoTable.
         /// </summary>
-        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
         /// <summary>
         /// Returns information about the node page.  Only the 0th node on each page has this property defined.
         /// </summary>
-        public XPathNodePageInfo PageInfo
+        public XPathNodePageInfo? PageInfo
         {
             get { return _pageInfo; }
         }
@@ -175,7 +176,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Return the base Uri of nodes that share this information atom.
         /// </summary>
-        public string BaseUri
+        public string? BaseUri
         {
             get { return _baseUri; }
         }
@@ -183,7 +184,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Return the page containing the next sibling of nodes that share this information atom.
         /// </summary>
-        public XPathNode[] SiblingPage
+        public XPathNode[]? SiblingPage
         {
             get { return _pageSibling; }
         }
@@ -191,7 +192,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Return the page containing the next element having a name which has same hashcode as this element.
         /// </summary>
-        public XPathNode[] SimilarElementPage
+        public XPathNode[]? SimilarElementPage
         {
             get { return _pageSimilar; }
         }
@@ -199,7 +200,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Return the page containing the parent of nodes that share this information atom.
         /// </summary>
-        public XPathNode[] ParentPage
+        public XPathNode[]? ParentPage
         {
             get { return _pageParent; }
         }
@@ -239,7 +240,7 @@ namespace MS.Internal.Xml.Cache
         /// <summary>
         /// Link together InfoAtoms that hash to the same hashtable bucket (should only be used by XPathNodeInfoTable)
         /// </summary>
-        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
         /// <summary>
         /// Return true if this InfoAtom has the same values as another InfoAtom.
         /// </summary>
-        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
         /// <summary>
         /// Create a new XNodeInfoAtom and ensure it is atomized in the table.
         /// </summary>
-        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
         /// </summary>
         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++)
             {