/// <summary> /// Calculate starting xml states that will result when iterating over and constructing an expression of the specified type. /// </summary> private void StartLoop(XmlQueryType typ, XmlILConstructInfo info) { Debug.Assert(!typ.IsSingleton); // This is tricky, because the looping introduces a feedback loop: // 1. Because loops may be executed many times, the beginning set of states must include the ending set of states. // 2. Because loops may be executed 0 times, the final set of states after all looping is complete must include // the initial set of states. // // +-- states-initial // | | // | states-begin-loop <--+ // | | | // | +--------------+ | // | | Construction | | // | +--------------+ | // | | | // | states-end-loop ----+ // | | // +--> states-final // Save starting loop states info.BeginLoopStates = this.xstates; if (typ.MaybeMany) { // If transition might occur from EnumAttrs to WithinContent, then states-end might be WithinContent, which // means states-begin needs to also include WithinContent. if (this.xstates == PossibleXmlStates.EnumAttrs && MaybeContent(typ)) { info.BeginLoopStates = this.xstates = PossibleXmlStates.Any; } } }
/// <summary> /// Analyze choice. /// </summary> protected virtual void AnalyzeChoice(QilChoice ndChoice, XmlILConstructInfo info) { PossibleXmlStates xstatesChoice; int idx; // Visit default branch; save resulting states idx = ndChoice.Branches.Count - 1; ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); xstatesChoice = this.xstates; // Visit all other branches while (--idx >= 0) { // Restore starting states and visit the next branch this.xstates = info.InitialStates; ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); // Choice ending states consist of combination of all branch states if (xstatesChoice != this.xstates) { xstatesChoice = PossibleXmlStates.Any; } } this.xstates = xstatesChoice; }
/// <summary> /// Recursively analyze the definition of a function. /// </summary> private static void AnalyzeDefinition(QilNode nd) { Debug.Assert(XmlILConstructInfo.Read(nd).PushToWriterLast, "Only need to analyze expressions which will be compiled in push mode."); switch (nd.NodeType) { case QilNodeType.Invoke: // Invoke node can either be compiled as IteratorThenWriter, or Writer. // Since IteratorThenWriter involves caching the results of the function call // and iterating over them, .tailcall cannot be used if (XmlILConstructInfo.Read(nd).ConstructMethod == XmlILConstructMethod.Writer) { OptimizerPatterns.Write(nd).AddPattern(OptimizerPatternName.TailCall); } break; case QilNodeType.Loop: { // Recursively analyze Loop return value QilLoop ndLoop = (QilLoop)nd; if (ndLoop.Variable.NodeType == QilNodeType.Let || !ndLoop.Variable.Binding.XmlType.MaybeMany) { AnalyzeDefinition(ndLoop.Body); } break; } case QilNodeType.Sequence: { // Recursively analyze last expression in Sequence QilList ndSeq = (QilList)nd; if (ndSeq.Count > 0) { AnalyzeDefinition(ndSeq[ndSeq.Count - 1]); } break; } case QilNodeType.Choice: { // Recursively analyze Choice branches QilChoice ndChoice = (QilChoice)nd; for (int i = 0; i < ndChoice.Branches.Count; i++) { AnalyzeDefinition(ndChoice.Branches[i]); } break; } case QilNodeType.Conditional: { // Recursively analyze Conditional branches QilTernary ndCond = (QilTernary)nd; AnalyzeDefinition(ndCond.Center); AnalyzeDefinition(ndCond.Right); break; } case QilNodeType.Nop: AnalyzeDefinition(((QilUnary)nd).Child); break; } }
/// <summary> /// Analyze loop. /// </summary> protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc. if (ndLoop.XmlType.MaybeMany) { CheckAttributeNamespaceConstruct(ndLoop.XmlType); } base.AnalyzeLoop(ndLoop, info); }
/// <summary> /// Analyze list. /// </summary> protected virtual void AnalyzeSequence(QilList ndSeq, XmlILConstructInfo info) { // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; // Analyze each item in the list for (int idx = 0; idx < ndSeq.Count; idx++) { ndSeq[idx] = AnalyzeContent(ndSeq[idx]); } }
/// <summary> /// Perform tail-call analysis on the functions in the specified QilExpression. /// </summary> public static void Analyze(QilExpression qil) { foreach (QilFunction ndFunc in qil.FunctionList) { // Only analyze functions which are pushed to the writer, since otherwise code // is generated after the call instruction in order to process cached results if (XmlILConstructInfo.Read(ndFunc).ConstructMethod == XmlILConstructMethod.Writer) { AnalyzeDefinition(ndFunc.Definition); } } }
/// <summary> /// Default to worst possible construction information. /// </summary> private XmlILConstructInfo(QilNodeType nodeType) { _nodeType = nodeType; _xstatesInitial = _xstatesFinal = PossibleXmlStates.Any; _xstatesBeginLoop = _xstatesEndLoop = PossibleXmlStates.None; _isNmspInScope = false; _mightHaveNmsp = true; _mightHaveAttrs = true; _mightHaveDupAttrs = true; _mightHaveNmspAfterAttrs = true; _constrMeth = XmlILConstructMethod.Iterator; _parentInfo = null; }
/// <summary> /// Calculate ending xml states that will result when iterating over and constructing an expression of the specified type. /// </summary> private void EndLoop(XmlQueryType typ, XmlILConstructInfo info) { Debug.Assert(!typ.IsSingleton); // Save ending loop states info.EndLoopStates = this.xstates; // If it's possible to loop zero times, then states-final needs to include states-initial if (typ.MaybeEmpty && info.InitialStates != this.xstates) { this.xstates = PossibleXmlStates.Any; } }
/// <summary> /// Analyze copying items. /// </summary> protected override void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) { if (ndCopy.NodeType == QilNodeType.AttributeCtor) { AnalyzeAttributeCtor(ndCopy as QilBinary, info); } else { CheckAttributeNamespaceConstruct(ndCopy.XmlType); } base.AnalyzeCopy(ndCopy, info); }
/// <summary> /// Create and initialize XmlILConstructInfo annotation for the specified node. /// </summary> public static XmlILConstructInfo Write(QilNode nd) { XmlILAnnotation ann = XmlILAnnotation.Write(nd); XmlILConstructInfo constrInfo = ann.ConstructInfo; if (constrInfo == null || constrInfo._isReadOnly) { constrInfo = new XmlILConstructInfo(nd.NodeType); ann.ConstructInfo = constrInfo; } return(constrInfo); }
/// <summary> /// Analyze copying items. /// </summary> protected override void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) { if (ndCopy.NodeType == QilNodeType.AttributeCtor) { QilBinary?binaryNode = ndCopy as QilBinary; Debug.Assert(binaryNode != null); AnalyzeAttributeCtor(binaryNode, info); } else { CheckAttributeNamespaceConstruct(ndCopy.XmlType !); } base.AnalyzeCopy(ndCopy, info); }
/// <summary> /// Analyze attribute constructor. /// </summary> private void AnalyzeAttributeCtor(QilBinary ndAttr, XmlILConstructInfo info) { if (ndAttr.Left.NodeType == QilNodeType.LiteralQName) { QilName?ndName = ndAttr.Left as QilName; Debug.Assert(ndName != null); XmlQualifiedName qname; int idx; // This attribute might be constructed on the parent element this.parentInfo !.MightHaveAttributes = true; // Check to see whether this attribute is a duplicate of a previous attribute if (!this.parentInfo.MightHaveDuplicateAttributes) { qname = new XmlQualifiedName(_attrNames.Add(ndName.LocalName), _attrNames.Add(ndName.NamespaceUri)); for (idx = 0; idx < _dupAttrs.Count; idx++) { XmlQualifiedName qnameDup = (XmlQualifiedName)_dupAttrs[idx] !; if ((object)qnameDup.Name == (object)qname.Name && (object)qnameDup.Namespace == (object)qname.Namespace) { // A duplicate attribute has been encountered this.parentInfo.MightHaveDuplicateAttributes = true; } } if (idx >= _dupAttrs.Count) { // This is not a duplicate attribute, so add it to the set _dupAttrs.Add(qname); } } // The attribute's namespace might need to be declared if (!info.IsNamespaceInScope) { this.parentInfo.MightHaveNamespaces = true; } } else { // Attribute prefix and namespace are not known at compile-time CheckAttributeNamespaceConstruct(ndAttr.XmlType !); } }
/// <summary> /// Get ConstructInfo annotation for the specified node. Lazily create if necessary. /// </summary> public static XmlILConstructInfo Read(QilNode nd) { XmlILAnnotation ann = nd.Annotation as XmlILAnnotation; XmlILConstructInfo constrInfo = (ann != null) ? ann.ConstructInfo : null; if (constrInfo == null) { if (Default == null) { constrInfo = new XmlILConstructInfo(QilNodeType.Unknown); constrInfo.isReadOnly = true; Default = constrInfo; } else { constrInfo = Default; } } return constrInfo; }
/// <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> /// Analyze loop. /// </summary> protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { XmlQueryType typ = ndLoop.XmlType; // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; if (!typ.IsSingleton) { StartLoop(typ, info); } // Body constructs content ndLoop.Body = AnalyzeContent(ndLoop.Body); if (!typ.IsSingleton) { EndLoop(typ, info); } }
/// <summary> /// Analyze conditional. /// </summary> protected virtual void AnalyzeConditional(QilTernary ndCond, XmlILConstructInfo info) { PossibleXmlStates xstatesTrue; // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; // Visit true branch; save resulting states ndCond.Center = AnalyzeContent(ndCond.Center); xstatesTrue = this.xstates; // Restore starting states and visit false branch this.xstates = info.InitialStates; ndCond.Right = AnalyzeContent(ndCond.Right); // Conditional ending states consist of combination of true and false branch states if (xstatesTrue != this.xstates) { this.xstates = PossibleXmlStates.Any; } }
/// <summary> /// Get ConstructInfo annotation for the specified node. Lazily create if necessary. /// </summary> public static XmlILConstructInfo Read(QilNode nd) { XmlILAnnotation ann = nd.Annotation as XmlILAnnotation; XmlILConstructInfo constrInfo = (ann != null) ? ann.ConstructInfo : null; if (constrInfo == null) { if (s_default == null) { constrInfo = new XmlILConstructInfo(QilNodeType.Unknown); constrInfo._isReadOnly = true; s_default = constrInfo; } else { constrInfo = s_default; } } return(constrInfo); }
/// <summary> /// Analyze copying items. /// </summary> protected virtual void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) { XmlQueryType typ = ndCopy.XmlType; // Copying item(s) to output involves looping if there is not exactly one item in the sequence if (!typ.IsSingleton) { StartLoop(typ, info); } // Determine state transitions that may take place if (MaybeContent(typ)) { if (MaybeAttrNmsp(typ)) { // Node might be Attr/Nmsp or non-Attr/Nmsp, so transition from EnumAttrs to WithinContent *may* occur if (this.xstates == PossibleXmlStates.EnumAttrs) { this.xstates = PossibleXmlStates.Any; } } else { // Node is guaranteed not to be Attr/Nmsp, so transition to WithinContent will occur if starting // state is EnumAttrs or if constructing within an element (guaranteed to be in EnumAttrs or WithinContent state) if (this.xstates == PossibleXmlStates.EnumAttrs || this.withinElem) { this.xstates = PossibleXmlStates.WithinContent; } } } if (!typ.IsSingleton) { EndLoop(typ, info); } }
/// <summary> /// Analyze loop. /// </summary> protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { XmlQueryType typ = ndLoop.XmlType; // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; if (!typ.IsSingleton) StartLoop(typ, info); // Body constructs content ndLoop.Body = AnalyzeContent(ndLoop.Body); if (!typ.IsSingleton) EndLoop(typ, info); }
/// <summary> /// Analyze list. /// </summary> protected virtual void AnalyzeSequence(QilList ndSeq, XmlILConstructInfo info) { // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; // Analyze each item in the list for (int idx = 0; idx < ndSeq.Count; idx++) ndSeq[idx] = AnalyzeContent(ndSeq[idx]); }
/// <summary> /// Analyze conditional. /// </summary> protected virtual void AnalyzeConditional(QilTernary ndCond, XmlILConstructInfo info) { PossibleXmlStates xstatesTrue; // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; // Visit true branch; save resulting states ndCond.Center = AnalyzeContent(ndCond.Center); xstatesTrue = this.xstates; // Restore starting states and visit false branch this.xstates = info.InitialStates; ndCond.Right = AnalyzeContent(ndCond.Right); // Conditional ending states consist of combination of true and false branch states if (xstatesTrue != this.xstates) this.xstates = PossibleXmlStates.Any; }
/// <summary> /// Returns true if the specified element should cache attributes. /// </summary> private bool ElementCachesAttributes(XmlILConstructInfo info) { // Attributes will be cached if namespaces might be constructed after the attributes return info.MightHaveDuplicateAttributes || info.MightHaveNamespacesAfterAttributes; }
/// <summary> /// Analyze choice. /// </summary> protected virtual void AnalyzeChoice(QilChoice ndChoice, XmlILConstructInfo info) { PossibleXmlStates xstatesChoice; int idx; // Visit default branch; save resulting states idx = ndChoice.Branches.Count - 1; ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); xstatesChoice = this.xstates; // Visit all other branches while (--idx >= 0) { // Restore starting states and visit the next branch this.xstates = info.InitialStates; ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); // Choice ending states consist of combination of all branch states if (xstatesChoice != this.xstates) xstatesChoice = PossibleXmlStates.Any; } this.xstates = xstatesChoice; }
/// <summary> /// Analyze loop. /// </summary> protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc. if (ndLoop.XmlType.MaybeMany) CheckAttributeNamespaceConstruct(ndLoop.XmlType); base.AnalyzeLoop(ndLoop, info); }
/// <summary> /// Analyze copying items. /// </summary> protected virtual void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) { XmlQueryType typ = ndCopy.XmlType; // Copying item(s) to output involves looping if there is not exactly one item in the sequence if (!typ.IsSingleton) StartLoop(typ, info); // Determine state transitions that may take place if (MaybeContent(typ)) { if (MaybeAttrNmsp(typ)) { // Node might be Attr/Nmsp or non-Attr/Nmsp, so transition from EnumAttrs to WithinContent *may* occur if (this.xstates == PossibleXmlStates.EnumAttrs) this.xstates = PossibleXmlStates.Any; } else { // Node is guaranteed not to be Attr/Nmsp, so transition to WithinContent will occur if starting // state is EnumAttrs or if constructing within an element (guaranteed to be in EnumAttrs or WithinContent state) if (this.xstates == PossibleXmlStates.EnumAttrs || this.withinElem) this.xstates = PossibleXmlStates.WithinContent; } } if (!typ.IsSingleton) EndLoop(typ, info); }
/// <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> /// Return true if a runtime check needs to be made in order to transition into the EnumAttrs state. /// </summary> private bool CheckEnumAttrs(XmlILConstructInfo info) { switch (info.InitialStates) { case PossibleXmlStates.WithinSequence: case PossibleXmlStates.EnumAttrs: // Transition to EnumAttrs can be ensured at compile-time return false; } return true; }
/// <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> /// 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); }
/// <summary> /// Create and initialize XmlILConstructInfo annotation for the specified node. /// </summary> public static XmlILConstructInfo Write(QilNode nd) { XmlILAnnotation ann = XmlILAnnotation.Write(nd); XmlILConstructInfo constrInfo = ann.ConstructInfo; if (constrInfo == null || constrInfo.isReadOnly) { constrInfo = new XmlILConstructInfo(nd.NodeType); ann.ConstructInfo = constrInfo; } return constrInfo; }
/// <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> /// Analyze attribute constructor. /// </summary> private void AnalyzeAttributeCtor(QilBinary ndAttr, XmlILConstructInfo info) { if (ndAttr.Left.NodeType == QilNodeType.LiteralQName) { QilName ndName = ndAttr.Left as QilName; XmlQualifiedName qname; int idx; // This attribute might be constructed on the parent element this.parentInfo.MightHaveAttributes = true; // Check to see whether this attribute is a duplicate of a previous attribute if (!this.parentInfo.MightHaveDuplicateAttributes) { qname = new XmlQualifiedName(this.attrNames.Add(ndName.LocalName), this.attrNames.Add(ndName.NamespaceUri)); for (idx = 0; idx < this.dupAttrs.Count; idx++) { XmlQualifiedName qnameDup = (XmlQualifiedName) this.dupAttrs[idx]; if ((object) qnameDup.Name == (object) qname.Name && (object) qnameDup.Namespace == (object) qname.Namespace) { // A duplicate attribute has been encountered this.parentInfo.MightHaveDuplicateAttributes = true; } } if (idx >= this.dupAttrs.Count) { // This is not a duplicate attribute, so add it to the set this.dupAttrs.Add(qname); } } // The attribute's namespace might need to be declared if (!info.IsNamespaceInScope) this.parentInfo.MightHaveNamespaces = true; } else { // Attribute prefix and namespace are not known at compile-time CheckAttributeNamespaceConstruct(ndAttr.XmlType); } }
/// <summary> /// Default to worst possible construction information. /// </summary> private XmlILConstructInfo(QilNodeType nodeType) { this.nodeType = nodeType; this.xstatesInitial = this.xstatesFinal = PossibleXmlStates.Any; this.xstatesBeginLoop = this.xstatesEndLoop = PossibleXmlStates.None; this.isNmspInScope = false; this.mightHaveNmsp = true; this.mightHaveAttrs = true; this.mightHaveDupAttrs = true; this.mightHaveNmspAfterAttrs = true; this.constrMeth = XmlILConstructMethod.Iterator; this.parentInfo = null; }
/// <summary> /// Calculate starting xml states that will result when iterating over and constructing an expression of the specified type. /// </summary> private void StartLoop(XmlQueryType typ, XmlILConstructInfo info) { Debug.Assert(!typ.IsSingleton); // This is tricky, because the looping introduces a feedback loop: // 1. Because loops may be executed many times, the beginning set of states must include the ending set of states. // 2. Because loops may be executed 0 times, the final set of states after all looping is complete must include // the initial set of states. // // +-- states-initial // | | // | states-begin-loop <--+ // | | | // | +--------------+ | // | | Construction | | // | +--------------+ | // | | | // | states-end-loop ----+ // | | // +--> states-final // Save starting loop states info.BeginLoopStates = this.xstates; if (typ.MaybeMany) { // If transition might occur from EnumAttrs to WithinContent, then states-end might be WithinContent, which // means states-begin needs to also include WithinContent. if (this.xstates == PossibleXmlStates.EnumAttrs && MaybeContent(typ)) info.BeginLoopStates = this.xstates = PossibleXmlStates.Any; } }
/// <summary> /// Returns true if the specified node's owner element might have local namespaces added to it /// after attributes have already been added. /// </summary> private bool MightHaveNamespacesAfterAttributes(XmlILConstructInfo info) { // Get parent element if (info != null) info = info.ParentElementInfo; // If a parent element has not been statically identified, then assume that the runtime // element will have namespaces added after attributes. if (info == null) return true; return info.MightHaveNamespacesAfterAttributes; }
/// <summary> /// Calculate ending xml states that will result when iterating over and constructing an expression of the specified type. /// </summary> private void EndLoop(XmlQueryType typ, XmlILConstructInfo info) { Debug.Assert(!typ.IsSingleton); // Save ending loop states info.EndLoopStates = this.xstates; // If it's possible to loop zero times, then states-final needs to include states-initial if (typ.MaybeEmpty && info.InitialStates != this.xstates) this.xstates = PossibleXmlStates.Any; }