/// <summary> /// Analyze the content argument of the ElementCtor. Try to eliminate as many runtime checks as possible, /// both for the ElementCtor and for content constructors. /// </summary> public override QilNode Analyze(QilNode ndElem, QilNode ndContent) { Debug.Assert(ndElem.NodeType == QilNodeType.ElementCtor); this.parentInfo = XmlILConstructInfo.Write(ndElem); // Start by assuming that these properties are false (they default to true, but analyzer might be able to // prove they are really false). this.parentInfo.MightHaveNamespacesAfterAttributes = false; this.parentInfo.MightHaveAttributes = false; this.parentInfo.MightHaveDuplicateAttributes = false; // The element's namespace might need to be declared this.parentInfo.MightHaveNamespaces = !this.parentInfo.IsNamespaceInScope; // Clear list of duplicate attributes _dupAttrs.Clear(); return(base.Analyze(ndElem, ndContent)); }
/// <summary> /// Perform analysis on the specified constructor and its content. Return the ndContent that was passed in, /// or a replacement. /// </summary> public virtual QilNode Analyze(QilNode ndConstr, QilNode ndContent) { if (ndConstr == null) { // Root expression is analyzed this.parentInfo = null; this.xstates = PossibleXmlStates.WithinSequence; this.withinElem = false; Debug.Assert(ndContent != null); ndContent = AnalyzeContent(ndContent); } else { this.parentInfo = XmlILConstructInfo.Write(ndConstr); if (ndConstr.NodeType == QilNodeType.Function) { // Results of function should be pushed to writer this.parentInfo.ConstructMethod = XmlILConstructMethod.Writer; // Start with PossibleXmlStates.None and then add additional possible starting states PossibleXmlStates xstates = PossibleXmlStates.None; foreach (XmlILConstructInfo infoCaller in this.parentInfo.CallersInfo) { if (xstates == PossibleXmlStates.None) { xstates = infoCaller.InitialStates; } else if (xstates != infoCaller.InitialStates) { xstates = PossibleXmlStates.Any; } // Function's results are pushed to Writer, so make sure that Invoke nodes' construct methods match infoCaller.PushToWriterFirst = true; } this.parentInfo.InitialStates = xstates; } else { // Build a standalone tree, with this constructor as its root if (ndConstr.NodeType != QilNodeType.Choice) { this.parentInfo.InitialStates = this.parentInfo.FinalStates = PossibleXmlStates.WithinSequence; } // Don't stream Rtf; fully cache the Rtf and copy it into any containing tree in order to simplify XmlILVisitor.VisitRtfCtor if (ndConstr.NodeType != QilNodeType.RtfCtor) { this.parentInfo.ConstructMethod = XmlILConstructMethod.WriterThenIterator; } } // Set withinElem = true if analyzing element content this.withinElem = (ndConstr.NodeType == QilNodeType.ElementCtor); switch (ndConstr.NodeType) { case QilNodeType.DocumentCtor: this.xstates = PossibleXmlStates.WithinContent; break; case QilNodeType.ElementCtor: this.xstates = PossibleXmlStates.EnumAttrs; break; case QilNodeType.AttributeCtor: this.xstates = PossibleXmlStates.WithinAttr; break; case QilNodeType.NamespaceDecl: Debug.Assert(ndContent == null); break; case QilNodeType.TextCtor: Debug.Assert(ndContent == null); break; case QilNodeType.RawTextCtor: Debug.Assert(ndContent == null); break; case QilNodeType.CommentCtor: this.xstates = PossibleXmlStates.WithinComment; break; case QilNodeType.PICtor: this.xstates = PossibleXmlStates.WithinPI; break; case QilNodeType.XsltCopy: this.xstates = PossibleXmlStates.Any; break; case QilNodeType.XsltCopyOf: Debug.Assert(ndContent == null); break; case QilNodeType.Function: this.xstates = this.parentInfo.InitialStates; break; case QilNodeType.RtfCtor: this.xstates = PossibleXmlStates.WithinContent; break; case QilNodeType.Choice: this.xstates = PossibleXmlStates.Any; break; default: Debug.Assert(false, ndConstr.NodeType + " is not handled by XmlILStateAnalyzer."); break; } if (ndContent != null) { ndContent = AnalyzeContent(ndContent); } if (ndConstr.NodeType == QilNodeType.Choice) { AnalyzeChoice(ndConstr as QilChoice, this.parentInfo); } // Since Function will never be another node's content, set its final states here if (ndConstr.NodeType == QilNodeType.Function) { this.parentInfo.FinalStates = this.xstates; } } return(ndContent); }
/// <summary> /// Recursively analyze content. Return "nd" or a replacement for it. /// </summary> protected virtual QilNode AnalyzeContent(QilNode nd) { XmlILConstructInfo info; QilNode ndChild; // Handle special node-types that are replaced switch (nd.NodeType) { case QilNodeType.For: case QilNodeType.Let: case QilNodeType.Parameter: // Iterator references are shared and cannot be annotated directly with ConstructInfo, // so wrap them with Nop node. nd = this.fac.Nop(nd); break; } // Get node's ConstructInfo annotation info = XmlILConstructInfo.Write(nd); // Set node's guaranteed parent constructor info.ParentInfo = this.parentInfo; // Construct all content using the Writer info.PushToWriterLast = true; // Set states that are possible before expression is constructed info.InitialStates = this.xstates; switch (nd.NodeType) { case QilNodeType.Loop: AnalyzeLoop(nd as QilLoop, info); break; case QilNodeType.Sequence: AnalyzeSequence(nd as QilList, info); break; case QilNodeType.Conditional: AnalyzeConditional(nd as QilTernary, info); break; case QilNodeType.Choice: AnalyzeChoice(nd as QilChoice, info); break; case QilNodeType.Error: case QilNodeType.Warning: // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; break; case QilNodeType.Nop: ndChild = (nd as QilUnary).Child; switch (ndChild.NodeType) { case QilNodeType.For: case QilNodeType.Let: case QilNodeType.Parameter: // Copy iterator items as content AnalyzeCopy(nd, info); break; default: // Ensure that construct method is Writer and recursively analyze content info.ConstructMethod = XmlILConstructMethod.Writer; AnalyzeContent(ndChild); break; } break; default: AnalyzeCopy(nd, info); break; } // Set states that are possible after expression is constructed info.FinalStates = this.xstates; return(nd); }
/// <summary> /// Determine whether an ElementCtor, AttributeCtor, or NamespaceDecl's namespace is already declared. If it is, /// set the IsNamespaceInScope property to True. Otherwise, add the namespace to the set of in-scope namespaces if /// addInScopeNmsp is True. Return false if the name is computed or is invalid. /// </summary> private bool CheckNamespaceInScope(QilBinary nd) { QilName ndName; string prefix, ns, prefixExisting, nsExisting; XPathNodeType nodeType; switch (nd.NodeType) { case QilNodeType.ElementCtor: case QilNodeType.AttributeCtor: ndName = nd.Left as QilName; if (ndName != null) { prefix = ndName.Prefix; ns = ndName.NamespaceUri; nodeType = (nd.NodeType == QilNodeType.ElementCtor) ? XPathNodeType.Element : XPathNodeType.Attribute; break; } // Not a literal name, so return false return(false); default: Debug.Assert(nd.NodeType == QilNodeType.NamespaceDecl); prefix = (string)(QilLiteral)nd.Left; ns = (string)(QilLiteral)nd.Right; nodeType = XPathNodeType.Namespace; break; } // Attribute with null namespace and xmlns:xml are always in-scope if (nd.NodeType == QilNodeType.AttributeCtor && ns.Length == 0 || prefix == "xml" && ns == XmlReservedNs.NsXml) { XmlILConstructInfo.Write(nd).IsNamespaceInScope = true; return(true); } // Don't process names that are invalid if (!ValidateNames.ValidateName(prefix, string.Empty, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping)) { return(false); } // Atomize names prefix = _nsmgr.NameTable.Add(prefix); ns = _nsmgr.NameTable.Add(ns); // Determine whether namespace is already in-scope for (int iNmsp = 0; iNmsp < _cntNmsp; iNmsp++) { _nsmgr.GetNamespaceDeclaration(iNmsp, out prefixExisting, out nsExisting); // If prefix is already declared, if ((object)prefix == (object)prefixExisting) { // Then if the namespace is the same, this namespace is redundant if ((object)ns == (object)nsExisting) { XmlILConstructInfo.Write(nd).IsNamespaceInScope = true; } // Else quit searching, because any further matching prefixes will be hidden (not in-scope) Debug.Assert(nd.NodeType != QilNodeType.NamespaceDecl || !_nsmgr.HasNamespace(prefix) || _nsmgr.LookupNamespace(prefix) == ns, "Compilers must ensure that namespace declarations do not conflict with the namespace used by the element constructor."); break; } } // If not in-scope, then add if it's allowed if (_addInScopeNmsp) { _nsmgr.AddNamespace(prefix, ns); _cntNmsp++; } return(true); }