Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / Xslt / XslAstAnalyzer.cs
blob87f96419f905237d98fcfbded5a4d57ee320a28c
1 //------------------------------------------------------------------------------
2 // <copyright file="XslAstAnalyzer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 using System.Globalization;
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Xml.XPath;
12 using System.Xml.Xsl.Qil;
13 using System.Xml.Xsl.Runtime;
14 using System.Xml.Xsl.XPath;
16 namespace System.Xml.Xsl.Xslt {
17 using TypeFactory = XmlQueryTypeFactory;
18 using XPathFunctionInfo = XPathBuilder.FunctionInfo<XPathBuilder.FuncId>;
19 using XsltFunctionInfo = XPathBuilder.FunctionInfo<QilGenerator.FuncId>;
21 // ------------------------------- XslAstAnalyzer -------------------------------
23 internal class XslAstAnalyzer : XslVisitor<XslFlags> {
24 private CompilerScopeManager<VarPar> scope;
25 private Compiler compiler;
26 #if DEBUG
27 // List of all variables and parameters
28 private List<VarPar> allVarPars = new List<VarPar>();
29 #endif
30 private int forEachDepth = 0;
31 private XPathAnalyzer xpathAnalyzer;
32 private ProtoTemplate currentTemplate;
34 // Type donor of the last analyzed VarPar. Used for optimization of WithParam's.
35 private VarPar typeDonor;
37 // Template dependencies
38 // rev/fwd - Callee-to-Coller/Coller-to-Callee
39 // 0/1 - for-each depth
40 private Graph<ProtoTemplate> revCall0Graph = new Graph<ProtoTemplate>();
41 private Graph<ProtoTemplate> revCall1Graph = new Graph<ProtoTemplate>();
42 private Dictionary<Template, Stylesheet> fwdApplyImportsGraph = new Dictionary<Template, Stylesheet>();
43 private Dictionary<QilName, List<ProtoTemplate>> revApplyTemplatesGraph = new Dictionary<QilName, List<ProtoTemplate>>();
45 // Data flow graph
46 private Graph<VarPar> dataFlow = new Graph<VarPar>();
48 // Mapping (mode, param name) -> helper vertex in data flow graph
49 private Dictionary<ModeName, VarPar> applyTemplatesParams = new Dictionary<ModeName, VarPar>();
51 // ---------------------------------- Graph<V> ----------------------------------
52 /// <summary>
53 /// Represents a graph using hashtable of adjacency lists.
54 /// </summary>
55 /// <typeparam name="V">Vertex type</typeparam>
56 internal class Graph<V> : Dictionary<V, List<V>>
57 where V : XslNode
59 private static IList<V> empty = (new List<V>()).AsReadOnly();
61 public IEnumerable<V> GetAdjList(V v) {
62 List<V> adjList;
63 if (TryGetValue(v, out adjList) && adjList != null) {
64 return adjList;
66 return empty;
69 public void AddEdge(V v1, V v2) {
70 // Ignore loops
71 if ((object)v1 == (object)v2) {
72 return;
75 List<V> adjList;
76 if (!TryGetValue(v1, out adjList) || adjList == null) {
77 adjList = this[v1] = new List<V>();
80 // NOTE: We do not check for duplicate edges here
81 adjList.Add(v2);
82 if (!TryGetValue(v2, out adjList)) {
83 this[v2] = null;
86 Debug.WriteLineIf(DiagnosticsSwitches.XslTypeInference.TraceVerbose, v1.TraceName + " -> " + v2.TraceName);
89 public void PropagateFlag(XslFlags flag) {
90 // Clean Stop flags
91 foreach (V v in Keys) {
92 v.Flags &= ~XslFlags.Stop;
95 foreach (V v in Keys) {
96 if ((v.Flags & XslFlags.Stop) == 0) {
97 if ((v.Flags & flag) != 0) {
98 DepthFirstSearch(v, flag);
104 private void DepthFirstSearch(V v, XslFlags flag) {
105 Debug.Assert((v.Flags & XslFlags.Stop) == 0, "Already visited this vertex");
106 v.Flags |= (flag | XslFlags.Stop);
107 foreach (V u in GetAdjList(v)) {
108 if ((u.Flags & XslFlags.Stop) == 0) {
109 DepthFirstSearch(u, flag);
111 Debug.Assert((u.Flags & flag) == flag, "Flag was not set on an adjacent vertex");
116 internal struct ModeName {
117 public QilName Mode;
118 public QilName Name;
120 public ModeName(QilName mode, QilName name) {
121 this.Mode = mode;
122 this.Name = name;
125 public override int GetHashCode() {
126 return Mode.GetHashCode() ^ Name.GetHashCode();
130 public XslFlags Analyze(Compiler compiler) {
131 this.compiler = compiler;
132 this.scope = new CompilerScopeManager<VarPar>();
133 this.xpathAnalyzer = new XPathAnalyzer(compiler, scope);
135 // Add global parameters and variables to the scope, they are visible everywhere
136 foreach (VarPar par in compiler.ExternalPars) {
137 scope.AddVariable(par.Name, par);
139 foreach (VarPar var in compiler.GlobalVars) {
140 scope.AddVariable(var.Name, var);
143 // Visit global parameters and variables, but ignore calculated flags
144 foreach (VarPar par in compiler.ExternalPars) {
145 Visit(par);
146 par.Flags |= XslFlags.AnyType;
148 foreach (VarPar var in compiler.GlobalVars) {
149 Visit(var);
152 // Global "----" current/position/last flags
153 XslFlags result = XslFlags.None;
155 // Visit templates and attribute sets
156 foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
157 currentTemplate = tmpl;
158 result |= Visit(tmpl);
161 // At this point for every local parameter we know whether its default value could be used
162 // by one of the callers of its template. Update flags for local parameters accordingly.
163 foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
164 foreach (XslNode instr in tmpl.Content) {
165 // Take care of a bizarre case <xsl:template match="/" xml:space="preserve"> <xsl:param name="par"/>
166 if (instr.NodeType == XslNodeType.Text) {
167 continue;
169 if (instr.NodeType != XslNodeType.Param) {
170 break;
173 VarPar par = (VarPar)instr;
174 if ((par.Flags & XslFlags.MayBeDefault) != 0) {
175 par.Flags |= par.DefValueFlags;
180 // Infer XPath types for all variables and local parameters by propagating literal
181 // types Rtf, Nodeset, Node, Boolean, Number, String through the data flow graph.
182 for (int flag = (int)XslFlags.Rtf; flag != 0; flag >>= 1) {
183 dataFlow.PropagateFlag((XslFlags)flag);
185 dataFlow = null;
187 // We need to follow revCall0Graph graph to propagate focus flags. But first complete
188 // dependency graph with fwdApplyImportsGraph
189 foreach (KeyValuePair<Template, Stylesheet> pair in fwdApplyImportsGraph) {
190 foreach (Stylesheet import in pair.Value.Imports) {
191 AddImportDependencies(import, /*focusDonor:*/pair.Key);
194 fwdApplyImportsGraph = null; // Finaly done with this.
196 if ((result & XslFlags.Current) != 0) {
197 revCall0Graph.PropagateFlag(XslFlags.Current);
199 if ((result & XslFlags.Position) != 0) {
200 revCall0Graph.PropagateFlag(XslFlags.Position);
202 if ((result & XslFlags.Last) != 0) {
203 revCall0Graph.PropagateFlag(XslFlags.Last);
205 if ((result & XslFlags.SideEffects) != 0) {
206 PropagateSideEffectsFlag();
208 revCall0Graph = null;
209 revCall1Graph = null;
210 revApplyTemplatesGraph = null;
212 // We can do this only after all flags were propagated.
213 // Otherwise we can miss case when flag comes to template from attribute-set
214 FillModeFlags(compiler.Root.ModeFlags, compiler.Root.Imports[0]);
216 TraceResults();
217 return result;
220 private void AddImportDependencies(Stylesheet sheet, Template focusDonor) {
221 foreach (Template tmpl in sheet.Templates) {
222 if (tmpl.Mode.Equals(focusDonor.Mode)) {
223 revCall0Graph.AddEdge(tmpl, focusDonor);
226 foreach (Stylesheet import in sheet.Imports) {
227 AddImportDependencies(import, focusDonor);
231 private void FillModeFlags(Dictionary<QilName, XslFlags> parentModeFlags, Stylesheet sheet) {
232 // Recursion: Process all imports to calculate ModeFlags for apply-import in this sheet
233 foreach (Stylesheet import in sheet.Imports) {
234 FillModeFlags(sheet.ModeFlags, import);
236 // My parrent depend on my my templates and templates imported
237 // 1. Copy ModeFlags of my imports to my parent
238 foreach (KeyValuePair<QilName, XslFlags> modeFlag in sheet.ModeFlags) {
239 XslFlags modeFlags;
240 if (! parentModeFlags.TryGetValue(modeFlag.Key, out modeFlags)) {
241 modeFlags = 0;
243 parentModeFlags[modeFlag.Key] = modeFlags | modeFlag.Value;
245 // 2. Add ModeFlags of my templates to my parent
246 foreach (Template tmpl in sheet.Templates) {
247 Debug.Assert(tmpl.Match != null);
248 XslFlags templateFlags = tmpl.Flags & (XslFlags.FocusFilter | XslFlags.SideEffects);
249 if (templateFlags != 0) {
250 XslFlags modeFlags;
251 if (! parentModeFlags.TryGetValue(tmpl.Mode, out modeFlags)) {
252 modeFlags = 0;
254 parentModeFlags[tmpl.Mode] = modeFlags | templateFlags;
259 private void TraceResults() {
260 #if DEBUG
261 if (DiagnosticsSwitches.XslTypeInference.TraceVerbose) {
262 Debug.WriteLine(string.Empty);
263 foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
264 Debug.WriteLine(tmpl.TraceName + " = " + (tmpl.Flags & XslFlags.FocusFilter));
267 Debug.WriteLine(string.Empty);
268 foreach (VarPar varPar in allVarPars) {
269 Debug.WriteLine(varPar.TraceName + " = " + (varPar.Flags & XslFlags.TypeFilter));
271 Debug.WriteLine(string.Empty);
274 if (DiagnosticsSwitches.XslTypeInference.TraceInfo) {
275 int current = 0, position = 0, last = 0;
277 foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
278 if ((tmpl.Flags & XslFlags.Current) != 0) {
279 current++;
281 if ((tmpl.Flags & XslFlags.Position) != 0) {
282 position++;
284 if ((tmpl.Flags & XslFlags.Last) != 0) {
285 last++;
289 int stringType = 0, numberType = 0, booleanType = 0, nodeNotRtfType = 0, nodesetNotRtfType = 0;
290 int nodeType = 0, nodesetType = 0, noneType = 0, anyType = 0, totalVarPars = 0;
292 foreach (VarPar varPar in allVarPars) {
293 switch (varPar.Flags & XslFlags.TypeFilter) {
294 case XslFlags.String : stringType++; break;
295 case XslFlags.Number : numberType++; break;
296 case XslFlags.Boolean : booleanType++; break;
297 case XslFlags.Node : nodeNotRtfType++; break;
298 case XslFlags.Nodeset : nodesetNotRtfType++; break;
299 case XslFlags.Rtf : nodeType++; break;
300 case XslFlags.Node | XslFlags.Rtf : nodeType++; break;
301 case XslFlags.Node | XslFlags.Nodeset : nodesetNotRtfType++; break;
302 case XslFlags.Nodeset | XslFlags.Rtf : nodesetType++; break;
303 case XslFlags.Node | XslFlags.Nodeset | XslFlags.Rtf : nodesetType++; break;
304 case XslFlags.None : noneType++; break;
305 default : anyType++; break;
307 totalVarPars++;
310 Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
311 "Total => templates/attribute-sets: {0}, variables/parameters: {1}.",
312 compiler.AllTemplates.Count, totalVarPars
315 Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
316 "Inferred focus => current: {0}, position: {1}, last: {2}.",
317 current, position, last
320 Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
321 "Inferred types => string: {0}, number: {1}, boolean: {2}, node: {3}, node-set: {4}, " +
322 "node-or-rtf: {5}, node-set-or-rtf: {6}, none: {7}, any: {8}.",
323 stringType, numberType, booleanType, nodeNotRtfType, nodesetNotRtfType,
324 nodeType, nodesetType, noneType, anyType
327 #endif
330 protected override XslFlags Visit(XslNode node) {
331 scope.EnterScope(node.Namespaces);
332 XslFlags result = base.Visit(node);
333 scope.ExitScope();
335 // Local variables and parameters must be added to the outer scope
336 if (currentTemplate != null && (node.NodeType == XslNodeType.Variable || node.NodeType == XslNodeType.Param)) {
337 scope.AddVariable(node.Name, (VarPar)node);
339 Debug.Assert(
340 (result & XslFlags.TypeFilter & ~XslFlags.Rtf) == 0,
341 "Instructions always return Rtf. node=" + node.NodeType.ToString() + " result=" + result.ToString()
343 return result;
346 protected override XslFlags VisitChildren(XslNode node) {
347 XslFlags result = XslFlags.None;
348 foreach (var child in node.Content) {
349 // Visit this child (recurses)
350 result |= this.Visit(child);
352 return result;
355 protected override XslFlags VisitAttributeSet(AttributeSet node) {
356 // @use-attribute-sets was processed into a sequence of UseAttributeSet nodes,
357 // which were prepended to the content of node
358 node.Flags = VisitChildren(node);
359 return node.Flags;
362 protected override XslFlags VisitTemplate(Template node) {
363 // @match does not affect any flags
364 //ProcessPattern(match);
365 node.Flags = VisitChildren(node);
366 return node.Flags;
369 protected override XslFlags VisitApplyImports(XslNode node) {
370 Debug.Assert(this.forEachDepth == 0, "xsl:apply-imports cannot be inside of xsl:for-each");
371 Debug.Assert(currentTemplate is Template, "xsl:apply-imports can only occur within xsl:template");
372 fwdApplyImportsGraph[(Template)currentTemplate] = (Stylesheet)node.Arg;
373 // xsl:apply-imports uses context node and is not in context of any for-each so it requires current
374 return XslFlags.HasCalls | XslFlags.Current | XslFlags.Rtf;
377 protected override XslFlags VisitApplyTemplates(XslNode node) {
378 Debug.Assert(node.Select != null, "Absent @select should be replaced with 'node()' in XsltLoader");
379 XslFlags result = ProcessExpr(node.Select);
381 foreach (XslNode instr in node.Content) {
382 result |= Visit(instr);
383 if (instr.NodeType == XslNodeType.WithParam) {
384 ModeName mn = new ModeName(/*mode:*/node.Name, instr.Name);
385 VarPar modePar;
387 if (!applyTemplatesParams.TryGetValue(mn, out modePar)) {
388 modePar = applyTemplatesParams[mn] = AstFactory.WithParam(instr.Name);
391 if (typeDonor != null) {
392 dataFlow.AddEdge(typeDonor, modePar);
393 } else {
394 modePar.Flags |= instr.Flags & XslFlags.TypeFilter;
399 if (currentTemplate != null) {
400 AddApplyTemplatesEdge(/*mode:*/node.Name, currentTemplate);
403 return XslFlags.HasCalls | XslFlags.Rtf | result;
406 protected override XslFlags VisitAttribute(NodeCtor node) {
407 return (
408 XslFlags.Rtf |
409 ProcessAvt(node.NameAvt) |
410 ProcessAvt(node.NsAvt) |
411 VisitChildren(node)
415 protected override XslFlags VisitCallTemplate(XslNode node) {
416 XslFlags result = XslFlags.None;
417 Template target;
419 if (!compiler.NamedTemplates.TryGetValue(node.Name, out target)) {
420 Debug.WriteLineIf(DiagnosticsSwitches.XslTypeInference.TraceError, "Unknown template " + node.Name.QualifiedName, "Error");
421 } else {
422 Debug.Assert(target != null);
423 if (currentTemplate != null) {
424 if (this.forEachDepth == 0) {
425 // ---- xsl:call-template, target would take its focus from currentTemplate
426 revCall0Graph.AddEdge(target, currentTemplate);
427 } else {
428 // in other cases we need it as donor for side effects flag
429 revCall1Graph.AddEdge(target, currentTemplate);
434 VarPar[] typeDonors = new VarPar[node.Content.Count];
435 int idx = 0;
437 foreach (XslNode instr in node.Content) {
438 Debug.Assert(instr.NodeType == XslNodeType.WithParam);
439 result |= Visit(instr);
440 typeDonors[idx++] = typeDonor;
443 // For each xsl:param in the target template find the corresponding xsl:with-param, and:
444 // a) if the type of xsl:with-param is known, add it to the type of xsl:param;
445 // b) if value of xsl:with-param is a VarPar reference, add an edge connecting it with xsl:param
446 // to the data flow graph.
448 if (target != null) {
449 foreach (XslNode instr in target.Content) {
450 // Take care of a bizarre case <xsl:template match="/" xml:space="preserve"> <xsl:param name="par"/>
451 if (instr.NodeType == XslNodeType.Text) {
452 continue;
454 if (instr.NodeType != XslNodeType.Param) {
455 break;
458 VarPar par = (VarPar)instr;
459 VarPar found = null;
460 idx = 0;
462 foreach (XslNode withPar in node.Content) {
463 if (withPar.Name.Equals(par.Name)) {
464 found = (VarPar)withPar;
465 typeDonor = typeDonors[idx];
466 break;
468 idx++;
471 if (found != null) {
472 // Found corresponding xsl:with-param, check its type
473 if (typeDonor != null) {
474 // add an edge from its type donor to xsl:param
475 dataFlow.AddEdge(typeDonor, par);
476 } else {
477 par.Flags |= found.Flags & XslFlags.TypeFilter;
479 } else {
480 // No value was specified for this xsl:param, default value will be used for it
481 par.Flags |= XslFlags.MayBeDefault;
486 return XslFlags.HasCalls | XslFlags.Rtf | result;
489 //protected override XslFlags VisitChoose(XslNode node) { return VisitChildren(node); }
491 protected override XslFlags VisitComment(XslNode node) {
492 return XslFlags.Rtf | VisitChildren(node);
495 protected override XslFlags VisitCopy(XslNode node) {
496 // @use-attribute-sets was processed into a sequence of UseAttributeSet nodes,
497 // which were prepended to the content of node
498 return XslFlags.Current | XslFlags.Rtf | VisitChildren(node);
501 protected override XslFlags VisitCopyOf(XslNode node) {
502 return XslFlags.Rtf | ProcessExpr(node.Select);
505 protected override XslFlags VisitElement(NodeCtor node) {
506 // @use-attribute-sets was processed into a sequence of UseAttributeSet nodes,
507 // which were prepended to the content of node
508 return (
509 XslFlags.Rtf |
510 ProcessAvt(node.NameAvt) |
511 ProcessAvt(node.NsAvt) |
512 VisitChildren(node)
516 protected override XslFlags VisitError(XslNode node) {
517 return (VisitChildren(node) & ~XslFlags.TypeFilter) | XslFlags.SideEffects;
520 protected override XslFlags VisitForEach(XslNode node) {
521 XslFlags result = ProcessExpr(node.Select);
522 this.forEachDepth ++;
523 foreach (XslNode child in node.Content) {
524 if (child.NodeType == XslNodeType.Sort) {
525 result |= Visit(child);
526 } else {
527 // Since for-each creates new focus, the focus flags of its children does not contribute into result
528 result |= Visit(child) & ~XslFlags.FocusFilter;
531 this.forEachDepth --;
532 return result;
535 protected override XslFlags VisitIf(XslNode node) {
536 return ProcessExpr(node.Select) | VisitChildren(node);
540 protected override XslFlags VisitKey(Key node) {
541 // @match and @use do not affect any flags
542 //ProcessPattern(node.Match);
543 //ProcessExpr(node.Use);
547 //protected override XslFlags VisitList(XslNode node) { return VisitChildren(node); }
549 protected override XslFlags VisitLiteralAttribute(XslNode node) {
550 return (
551 XslFlags.Rtf |
552 ProcessAvt(node.Select) |
553 VisitChildren(node)
557 protected override XslFlags VisitLiteralElement(XslNode node) {
558 return XslFlags.Rtf | VisitChildren(node);
561 protected override XslFlags VisitMessage(XslNode node) {
562 return (VisitChildren(node) & ~XslFlags.TypeFilter) | XslFlags.SideEffects;
565 //protected override XslFlags VisitNop(XslNode node) { return VisitChildren(node); }
567 protected override XslFlags VisitNumber(Number node) {
568 return (
569 XslFlags.Rtf |
570 ProcessPattern(node.Count) |
571 ProcessPattern(node.From) |
572 (node.Value != null ? ProcessExpr(node.Value) : XslFlags.Current) |
573 ProcessAvt(node.Format) |
574 ProcessAvt(node.Lang) |
575 ProcessAvt(node.LetterValue) |
576 ProcessAvt(node.GroupingSeparator) |
577 ProcessAvt(node.GroupingSize)
581 //protected override XslFlags VisitOtherwise(XslNode node) { return VisitChildren(node); }
583 protected override XslFlags VisitPI(XslNode node) {
584 return (
585 XslFlags.Rtf |
586 ProcessAvt(node.Select) |
587 VisitChildren(node)
591 protected override XslFlags VisitSort(Sort node) {
592 return (
593 // @select is calculated in context of xsl:for-each or xsl:apply-templates,
594 // so it does not affect focus flags
595 ProcessExpr(node.Select) & ~XslFlags.FocusFilter |
596 ProcessAvt(node.Lang) |
597 ProcessAvt(node.DataType) |
598 ProcessAvt(node.Order) |
599 ProcessAvt(node.CaseOrder)
603 protected override XslFlags VisitText(Text node) {
604 return XslFlags.Rtf | VisitChildren(node);
607 protected override XslFlags VisitUseAttributeSet(XslNode node) {
608 AttributeSet attSet;
609 if (!compiler.AttributeSets.TryGetValue(node.Name, out attSet)) {
610 Debug.WriteLineIf(DiagnosticsSwitches.XslTypeInference.TraceError, "Unknown attribute-set " + node.Name.QualifiedName, "Error");
611 } else if (currentTemplate != null) {
612 if (this.forEachDepth == 0) {
613 // ---- [xsl:]use-attribute-sets, attSet would take its focus from currentTemplate
614 revCall0Graph.AddEdge(attSet, currentTemplate);
615 } else {
616 // in other cases we need it as donor for side effects flag
617 revCall1Graph.AddEdge(attSet, currentTemplate);
620 return XslFlags.HasCalls | XslFlags.Rtf;
623 protected override XslFlags VisitValueOf(XslNode node) {
624 return XslFlags.Rtf | ProcessExpr(node.Select);
627 protected override XslFlags VisitValueOfDoe(XslNode node) {
628 return XslFlags.Rtf | ProcessExpr(node.Select);
631 protected override XslFlags VisitParam(VarPar node) {
632 Template tmpl = currentTemplate as Template;
633 if (tmpl != null && tmpl.Match != null) {
634 // This template has 'match' attribute and might be called from built-in template rules,
635 // all xsl:param's will be defaulted in that case
636 node.Flags |= XslFlags.MayBeDefault;
638 ModeName mn = new ModeName(tmpl.Mode, node.Name);
639 VarPar par;
641 if (!applyTemplatesParams.TryGetValue(mn, out par)) {
642 par = applyTemplatesParams[mn] = AstFactory.WithParam(node.Name);
644 dataFlow.AddEdge(par, node);
646 node.DefValueFlags = ProcessVarPar(node);
647 return node.DefValueFlags & ~XslFlags.TypeFilter;
650 protected override XslFlags VisitVariable(VarPar node) {
651 node.Flags = ProcessVarPar(node);
652 return node.Flags & ~XslFlags.TypeFilter;
655 protected override XslFlags VisitWithParam(VarPar node) {
656 node.Flags = ProcessVarPar(node);
657 return node.Flags & ~XslFlags.TypeFilter;
660 private XslFlags ProcessVarPar(VarPar node) {
661 XslFlags result;
662 #if DEBUG
663 if (node.NodeType != XslNodeType.WithParam) {
664 allVarPars.Add(node);
666 #endif
668 if (node.Select != null) {
669 if (node.Content.Count != 0) {
670 // In case of incorrect stylesheet, variable or parameter may have both a 'select' attribute and non-empty content
671 // NOTE: This code must be in sync with recovery logic in QilGenerator
672 result = xpathAnalyzer.Analyze(node.Select) | VisitChildren(node) | XslFlags.AnyType;
673 typeDonor = null;
674 } else {
675 result = xpathAnalyzer.Analyze(node.Select);
676 typeDonor = xpathAnalyzer.TypeDonor;
677 if (typeDonor != null && node.NodeType != XslNodeType.WithParam) {
678 dataFlow.AddEdge(typeDonor, node);
681 } else if (node.Content.Count != 0) {
682 result = XslFlags.Rtf | VisitChildren(node);
683 typeDonor = null;
684 } else {
685 result = XslFlags.String;
686 typeDonor = null;
688 return result;
691 // Ignores XPath type flags
692 private XslFlags ProcessExpr(string expr) {
693 return xpathAnalyzer.Analyze(expr) & ~XslFlags.TypeFilter;
696 // Ignores XPath type flags
697 private XslFlags ProcessAvt(string avt) {
698 return xpathAnalyzer.AnalyzeAvt(avt) & ~XslFlags.TypeFilter;
701 // Ignores XPath type flags and focus flags
702 private XslFlags ProcessPattern(string pattern) {
703 // We need to analyze using of variables in the pattern
704 return xpathAnalyzer.Analyze(pattern) & ~XslFlags.TypeFilter & ~XslFlags.FocusFilter;
707 private void AddApplyTemplatesEdge(QilName mode, ProtoTemplate dependentTemplate) {
708 List<ProtoTemplate> templates;
709 if (!revApplyTemplatesGraph.TryGetValue(mode, out templates)) {
710 templates = new List<ProtoTemplate>();
711 revApplyTemplatesGraph.Add(mode, templates);
712 } else {
713 if (templates[templates.Count - 1] == dependentTemplate) {
714 return; // this is a duplicate
718 templates.Add(dependentTemplate);
721 private void PropagateSideEffectsFlag() {
722 // Clean Stop flags
723 foreach (ProtoTemplate t in revCall0Graph.Keys) {
724 t.Flags &= ~XslFlags.Stop;
726 foreach (ProtoTemplate t in revCall1Graph.Keys) {
727 t.Flags &= ~XslFlags.Stop;
730 foreach (ProtoTemplate t in revCall0Graph.Keys) {
731 if ((t.Flags & XslFlags.Stop) == 0) {
732 if ((t.Flags & XslFlags.SideEffects) != 0) {
733 DepthFirstSearch(t);
737 foreach (ProtoTemplate t in revCall1Graph.Keys) {
738 if ((t.Flags & XslFlags.Stop) == 0) {
739 if ((t.Flags & XslFlags.SideEffects) != 0) {
740 DepthFirstSearch(t);
746 private void DepthFirstSearch(ProtoTemplate t) {
747 Debug.Assert((t.Flags & XslFlags.Stop) == 0, "Already visited this vertex");
748 t.Flags |= (XslFlags.SideEffects | XslFlags.Stop);
749 List<ProtoTemplate> list;
750 foreach (ProtoTemplate u in revCall0Graph.GetAdjList(t)) {
751 if ((u.Flags & XslFlags.Stop) == 0) {
752 DepthFirstSearch(u);
754 Debug.Assert((u.Flags & XslFlags.SideEffects) == XslFlags.SideEffects, "Flag was not set on an adjacent vertex");
756 foreach (ProtoTemplate u in revCall1Graph.GetAdjList(t)) {
757 if ((u.Flags & XslFlags.Stop) == 0) {
758 DepthFirstSearch(u);
760 Debug.Assert((u.Flags & XslFlags.SideEffects) == XslFlags.SideEffects, "Flag was not set on an adjacent vertex");
762 Template template = t as Template;
763 if (
764 template != null && // This ProteTemplate is Template
765 revApplyTemplatesGraph.TryGetValue(template.Mode, out list) // list - ProtoTemplates that have apply-templatess mode="{template.Mode}"
767 revApplyTemplatesGraph.Remove(template.Mode); // to prevent recursion remove this list from dictionary
768 foreach (ProtoTemplate u in list) {
769 if ((u.Flags & XslFlags.Stop) == 0) {
770 DepthFirstSearch(u);
772 Debug.Assert((u.Flags & XslFlags.SideEffects) == XslFlags.SideEffects, "Flag was not set on an adjacent vertex");
777 // ------------------------------- XPathAnalyzer --------------------------------
779 // Ignores all errors and warnings
780 internal struct NullErrorHelper : IErrorHelper {
781 public void ReportError(string res, params string[] args) { }
782 public void ReportWarning(string res, params string[] args) { }
785 internal class XPathAnalyzer : IXPathBuilder<XslFlags> {
786 private XPathParser<XslFlags> xpathParser = new XPathParser<XslFlags>();
787 private CompilerScopeManager<VarPar> scope;
788 private Compiler compiler;
790 // True if the expression needs XSLT's current() node
791 private bool xsltCurrentNeeded;
793 // If the expression is just a reference to some VarPar, like "(($foo))",
794 // then this field contains that VarPar, and null otherwise.
795 private VarPar typeDonor;
797 public VarPar TypeDonor {
798 get { return typeDonor; }
801 public XPathAnalyzer(Compiler compiler, CompilerScopeManager<VarPar> scope) {
802 this.compiler = compiler;
803 this.scope = scope;
806 public XslFlags Analyze(string xpathExpr) {
807 typeDonor = null;
808 if (xpathExpr == null) {
809 return XslFlags.None;
811 try {
812 // Note that the constructor may throw an exception, for example, in case of the expression "'"
813 xsltCurrentNeeded = false;
814 XPathScanner scanner = new XPathScanner(xpathExpr);
815 XslFlags result = xpathParser.Parse(scanner, this, LexKind.Eof);
816 if (xsltCurrentNeeded) {
817 result |= XslFlags.Current;
819 return result;
820 } catch (XslLoadException) {
821 return XslFlags.AnyType | XslFlags.FullFocus;
825 public XslFlags AnalyzeAvt(string source) {
826 typeDonor = null;
827 if (source == null) {
828 return XslFlags.None;
830 try {
831 xsltCurrentNeeded = false;
832 XslFlags result = XslFlags.None;
833 int pos = 0;
834 while (pos < source.Length) {
835 pos = source.IndexOf('{', pos);
836 if (pos == -1) {
837 break; // no more AVTs
839 pos++;
840 if (pos < source.Length && source[pos] == '{') { // "{{"
841 pos++;
842 continue;
844 if (pos < source.Length) { // '{' encountered, parse an expression
845 XPathScanner scanner = new XPathScanner(source, pos);
846 result |= xpathParser.Parse(scanner, this, LexKind.RBrace);
847 pos = scanner.LexStart + 1;
850 if (xsltCurrentNeeded) {
851 result |= XslFlags.Current;
853 return result & ~XslFlags.TypeFilter;
854 } catch (XslLoadException) {
855 return XslFlags.FullFocus;
859 // Returns null in case of error
860 private VarPar ResolveVariable(string prefix, string name) {
861 string ns = ResolvePrefix(prefix);
862 if (ns == null) {
863 return null;
865 return scope.LookupVariable(name, ns);
868 // Returns null in case of error
869 private string ResolvePrefix(string prefix) {
870 // ignoreDefaultNs == true
871 if (prefix.Length == 0) {
872 return string.Empty;
873 } else {
874 return scope.LookupNamespace(prefix);
878 public virtual void StartBuild() {
881 public virtual XslFlags EndBuild(XslFlags result) {
882 return result;
885 public virtual XslFlags String(string value) {
886 typeDonor = null;
887 return XslFlags.String;
890 public virtual XslFlags Number(double value) {
891 typeDonor = null;
892 return XslFlags.Number;
895 private static XslFlags[] OperatorType = {
896 /*Unknown */ XslFlags.AnyType,
897 /*Or */ XslFlags.Boolean,
898 /*And */ XslFlags.Boolean,
899 /*Eq */ XslFlags.Boolean,
900 /*Ne */ XslFlags.Boolean,
901 /*Lt */ XslFlags.Boolean,
902 /*Le */ XslFlags.Boolean,
903 /*Gt */ XslFlags.Boolean,
904 /*Ge */ XslFlags.Boolean,
905 /*Plus */ XslFlags.Number ,
906 /*Minus */ XslFlags.Number ,
907 /*Multiply */ XslFlags.Number ,
908 /*Divide */ XslFlags.Number ,
909 /*Modulo */ XslFlags.Number ,
910 /*UnaryMinus*/ XslFlags.Number ,
911 /*Union */ XslFlags.Nodeset,
914 public virtual XslFlags Operator(XPathOperator op, XslFlags left, XslFlags right) {
915 typeDonor = null;
916 Debug.Assert(op != XPathOperator.Unknown);
917 XslFlags result = (left | right) & ~XslFlags.TypeFilter;
918 return result | OperatorType[(int)op];
921 public virtual XslFlags Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string prefix, string name) {
922 typeDonor = null;
923 if (xpathAxis == XPathAxis.Self && nodeType == XPathNodeType.All && prefix == null && name == null) {
924 return XslFlags.Current | XslFlags.Node;
925 } else {
926 return XslFlags.Current | XslFlags.Nodeset;
930 // "left/right"
931 public virtual XslFlags JoinStep(XslFlags left, XslFlags right) {
932 typeDonor = null;
933 return (left & ~XslFlags.TypeFilter) | XslFlags.Nodeset; // "ex:Foo(position())/Bar"
936 // "nodeset[predicate]"
937 public virtual XslFlags Predicate(XslFlags nodeset, XslFlags predicate, bool isReverseStep) {
938 typeDonor = null;
939 return (nodeset & ~XslFlags.TypeFilter) | XslFlags.Nodeset | (predicate & XslFlags.SideEffects); // "ex:Foo(position())[Bar]"
942 public virtual XslFlags Variable(string prefix, string name) {
943 typeDonor = ResolveVariable(prefix, name);
944 if (typeDonor == null) {
945 Debug.WriteLineIf(DiagnosticsSwitches.XslTypeInference.TraceError, "Unresolved variable " + Compiler.ConstructQName(prefix, name), "Error");
946 return XslFlags.AnyType;
948 return XslFlags.None;
951 public virtual XslFlags Function(string prefix, string name, IList<XslFlags> args) {
952 typeDonor = null;
954 XslFlags argsFlags = XslFlags.None;
955 foreach (XslFlags t in args) {
956 argsFlags |= t;
959 XslFlags funcFlags = XslFlags.None;
961 if (prefix.Length == 0) {
962 XPathFunctionInfo xpathFunc;
963 XsltFunctionInfo xsltFunc;
965 if (XPathBuilder.FunctionTable.TryGetValue(name, out xpathFunc)) {
966 XPathBuilder.FuncId funcId = xpathFunc.id;
967 funcFlags = XPathFunctionFlags[(int)funcId];
968 if (args.Count == 0 && (
969 funcId == XPathBuilder.FuncId.LocalName ||
970 funcId == XPathBuilder.FuncId.NamespaceUri ||
971 funcId == XPathBuilder.FuncId.Name ||
972 funcId == XPathBuilder.FuncId.String ||
973 funcId == XPathBuilder.FuncId.Number ||
974 funcId == XPathBuilder.FuncId.StringLength ||
975 funcId == XPathBuilder.FuncId.Normalize
976 )) {
977 funcFlags |= XslFlags.Current;
979 } else if (QilGenerator.FunctionTable.TryGetValue(name, out xsltFunc)) {
980 QilGenerator.FuncId funcId = xsltFunc.id;
981 funcFlags = XsltFunctionFlags[(int)funcId];
982 if (funcId == QilGenerator.FuncId.Current) {
983 xsltCurrentNeeded = true;
984 } else if (funcId == QilGenerator.FuncId.GenerateId && args.Count == 0) {
985 funcFlags |= XslFlags.Current;
988 } else {
989 string ns = ResolvePrefix(prefix);
990 if (ns == XmlReservedNs.NsMsxsl) {
991 switch (name) {
992 case "node-set": funcFlags = XslFlags.Nodeset; break;
993 case "string-compare": funcFlags = XslFlags.Number; break;
994 case "utc": funcFlags = XslFlags.String; break;
995 case "format-date": funcFlags = XslFlags.String; break;
996 case "format-time": funcFlags = XslFlags.String; break;
997 case "local-name": funcFlags = XslFlags.String; break;
998 case "namespace-uri": funcFlags = XslFlags.String | XslFlags.Current; break;
999 case "number": funcFlags = XslFlags.Number; break;
1001 } else if (ns == XmlReservedNs.NsExsltCommon) {
1002 switch (name) {
1003 case "node-set": funcFlags = XslFlags.Nodeset; break;
1004 case "object-type": funcFlags = XslFlags.String; break;
1008 if (funcFlags == XslFlags.None) {
1009 // Unknown function. Can be script function or extension function
1010 funcFlags = XslFlags.AnyType;
1011 if (compiler.Settings.EnableScript && ns != null) {
1012 XmlExtensionFunction scrFunc = compiler.Scripts.ResolveFunction(name, ns, args.Count, new NullErrorHelper());
1013 if (scrFunc != null) {
1014 XmlQueryType xt = scrFunc.XmlReturnType;
1015 if (xt == TypeFactory.StringX) {
1016 funcFlags = XslFlags.String;
1017 } else if (xt == TypeFactory.DoubleX) {
1018 funcFlags = XslFlags.Number;
1019 } else if (xt == TypeFactory.BooleanX) {
1020 funcFlags = XslFlags.Boolean;
1021 } else if (xt == TypeFactory.NodeNotRtf) {
1022 funcFlags = XslFlags.Node;
1023 } else if (xt == TypeFactory.NodeSDod) {
1024 funcFlags = XslFlags.Nodeset;
1025 } else if (xt == TypeFactory.ItemS) {
1026 funcFlags = XslFlags.AnyType;
1027 } else if (xt == TypeFactory.Empty) {
1028 funcFlags = XslFlags.Nodeset;
1029 } else {
1030 Debug.Fail("Unexpected XmlQueryType for script function: " + xt.ToString());
1034 funcFlags |= XslFlags.SideEffects;
1038 return (argsFlags & ~XslFlags.TypeFilter) | funcFlags;
1041 #region XPath Function Flags
1042 private static XslFlags[] XPathFunctionFlags = {
1043 /*Last */ XslFlags.Number | XslFlags.Last,
1044 /*Position */ XslFlags.Number | XslFlags.Position,
1045 /*Count */ XslFlags.Number,
1046 /*LocalName */ XslFlags.String, // | XslFlags.Current if 0 args
1047 /*NamespaceUri */ XslFlags.String, // | XslFlags.Current if 0 args
1048 /*Name */ XslFlags.String, // | XslFlags.Current if 0 args
1049 /*String */ XslFlags.String, // | XslFlags.Current if 0 args
1050 /*Number */ XslFlags.Number, // | XslFlags.Current if 0 args
1051 /*Boolean */ XslFlags.Boolean,
1052 /*True */ XslFlags.Boolean,
1053 /*False */ XslFlags.Boolean,
1054 /*Not */ XslFlags.Boolean,
1055 /*Id */ XslFlags.Nodeset | XslFlags.Current,
1056 /*Concat */ XslFlags.String,
1057 /*StartsWith */ XslFlags.Boolean,
1058 /*Contains */ XslFlags.Boolean,
1059 /*SubstringBefore */ XslFlags.String,
1060 /*SubstringAfter */ XslFlags.String,
1061 /*Substring */ XslFlags.String,
1062 /*StringLength */ XslFlags.Number, // | XslFlags.Current if 0 args
1063 /*Normalize */ XslFlags.String, // | XslFlags.Current if 0 args
1064 /*Translate */ XslFlags.String,
1065 /*Lang */ XslFlags.Boolean | XslFlags.Current,
1066 /*Sum */ XslFlags.Number,
1067 /*Floor */ XslFlags.Number,
1068 /*Ceiling */ XslFlags.Number,
1069 /*Round */ XslFlags.Number,
1071 #endregion
1073 #region Xslt Function Flags
1074 private static XslFlags[] XsltFunctionFlags = {
1075 /*Current */ XslFlags.Node, // xsltCurrentNeeded = true
1076 /*Document */ XslFlags.Nodeset,
1077 /*Key */ XslFlags.Nodeset | XslFlags.Current,
1078 /*FormatNumber */ XslFlags.String,
1079 /*UnparsedEntityUri */ XslFlags.String, // | XslFlags.Current if it is implemented
1080 /*GenerateId */ XslFlags.String, // | XslFlags.Current if 0 args
1081 /*SystemProperty */ XslFlags.String | XslFlags.Number,
1082 /*ElementAvailable */ XslFlags.Boolean,
1083 /*FunctionAvailable */ XslFlags.Boolean,
1085 #endregion
1089 // ------------------------------- XslAstRewriter -------------------------------
1091 internal sealed class XslAstRewriter {
1093 private static readonly QilName nullMode = AstFactory.QName(string.Empty);
1095 private CompilerScopeManager<VarPar> scope;
1096 private Stack<Template> newTemplates;
1097 private Compiler compiler;
1099 public void Rewrite(Compiler compiler) {
1100 this.compiler = compiler;
1101 this.scope = new CompilerScopeManager<VarPar>();
1102 this.newTemplates = new Stack<Template>();
1104 // Rewrite every template
1105 foreach (var template in compiler.AllTemplates) {
1106 scope.EnterScope();
1107 CheckNodeCost(template);
1108 scope.CheckEmpty();
1111 // Add the new templates to the compiled set
1112 while (newTemplates.Count > 0) {
1113 var newtemplate = newTemplates.Pop();
1115 // From Stylesheet.AddTemplate(newtemplate):
1116 compiler.AllTemplates.Add(newtemplate);
1117 compiler.NamedTemplates.Add(newtemplate.Name, newtemplate);
1119 scope.EnterScope();
1120 CheckNodeCost(newtemplate);
1121 scope.CheckEmpty();
1125 // Returns a cost based on an estimate of the number of locals required for the given expression
1126 private static int NodeCostForXPath(string xpath) {
1127 int cost = 0;
1128 if (xpath != null) {
1129 // Every XPath expression needs at least one iterator
1130 cost = IteratorNodeCost;
1131 // Count slashes, two characters at a time, ignore leading slash
1132 for (int t = 2; t < xpath.Length; t += 2) {
1133 if (xpath[t] == '/' || xpath[t - 1] == '/') {
1134 cost += IteratorNodeCost;
1138 return cost;
1141 // These values should be changed to achieve methods with ~2KB IL
1142 const int FixedNodeCost = 1; // should probably depend on node type
1143 const int IteratorNodeCost = 2; // XPath iterators are more expensive
1144 const int CallTemplateCost = 1; // best kept at a minimum, 1
1145 const int RewriteThreshold = 100;
1147 // These are all the node types for which the .Select member has an XPath expression
1148 const int NodesWithSelect =
1149 (1 << (int)XslNodeType.Param) |
1150 (1 << (int)XslNodeType.Variable) |
1151 (1 << (int)XslNodeType.WithParam) |
1152 (1 << (int)XslNodeType.ApplyTemplates) |
1153 (1 << (int)XslNodeType.CopyOf) |
1154 (1 << (int)XslNodeType.ForEach) |
1155 (1 << (int)XslNodeType.If) |
1156 //(1 << (int)XslNodeType.Number) | // has XPath, but not in .Select member
1157 (1 << (int)XslNodeType.Sort) |
1158 (1 << (int)XslNodeType.ValueOf) |
1159 (1 << (int)XslNodeType.ValueOfDoe);
1161 // These are all the node types which can have call-template as a child and are therefor suitable for refactoring
1162 const int ParentsOfCallTemplate =
1163 (1 << (int)XslNodeType.Attribute) |
1164 (1 << (int)XslNodeType.Comment) |
1165 (1 << (int)XslNodeType.Copy) |
1166 (1 << (int)XslNodeType.Element) |
1167 (1 << (int)XslNodeType.ForEach) |
1168 (1 << (int)XslNodeType.If) | // also used for xsl:when
1169 (1 << (int)XslNodeType.Message) |
1170 (1 << (int)XslNodeType.Otherwise) |
1171 (1 << (int)XslNodeType.Param) |
1172 (1 << (int)XslNodeType.PI) |
1173 (1 << (int)XslNodeType.Template) |
1174 (1 << (int)XslNodeType.Variable) |
1175 (1 << (int)XslNodeType.WithParam) |
1176 (1 << (int)XslNodeType.LiteralAttribute) |
1177 (1 << (int)XslNodeType.LiteralElement);
1179 // Tests whether the specified XslNodeType bit is set in the provided flags
1180 private static bool NodeTypeTest(XslNodeType nodetype, int flags) {
1181 return ((flags >> (int)nodetype) & 1) != 0;
1184 private int CheckNodeCost(XslNode node) {
1185 scope.EnterScope(node.Namespaces);
1187 // We don't want to allow rewriting by default
1188 bool canRewrite = false;
1190 // Use a constant cost for all nodes (should probably depend on the node's type)
1191 int nodeCost = FixedNodeCost;
1193 // Detect the number of iterators used by the node's 'select' attribute
1194 if (NodeTypeTest(node.NodeType, NodesWithSelect)) {
1195 nodeCost += NodeCostForXPath(node.Select);
1198 // Iterate through all the child nodes
1199 var content = node.Content;
1200 int last = content.Count - 1;
1201 for (int t = 0; t <= last; ++t) {
1202 var child = content[t];
1204 var costForChild = CheckNodeCost(child); // recurse
1206 nodeCost += costForChild;
1207 if (canRewrite && nodeCost > RewriteThreshold) {
1208 // This child would overflow the limit for this scope; create a new scope
1210 // Don't refactor the code if this is the last child and its cost is trivial
1211 if (t < last || costForChild > CallTemplateCost) {
1213 //Debug.WriteLine("Node {0} (within {5}) on {1}:{2} has cost {3}; {4} total",
1214 // child.NodeType, child.SourceLine.Start.Line, child.SourceLine.Start.Pos, costForChild, nodeCost, node.NodeType);
1216 // Refactor this node, moving the current child and all after into a new template
1217 Refactor(node, t);
1219 // The new template (containing the remainder of the current node) will be processed later
1220 nodeCost -= costForChild;
1221 nodeCost += CallTemplateCost;
1223 break;
1226 // Local variables and parameters must be added to the outer scope
1227 if (child.NodeType == XslNodeType.Variable || child.NodeType == XslNodeType.Param) {
1228 scope.AddVariable(child.Name, (VarPar)child);
1229 // Parameters will cause code generation at the call-site, not in the callee
1230 if (child.NodeType == XslNodeType.Param) {
1231 nodeCost -= costForChild;
1234 else if (!canRewrite) {
1235 // We're passed the parameters and our first real node; start checking the cost
1236 // Note: some nodes like xsl:choose cannot contain xsl:call-template
1237 canRewrite = NodeTypeTest(node.NodeType, ParentsOfCallTemplate);
1241 scope.ExitScope();
1242 return nodeCost;
1245 // Splits the children into two pieces: prefix and suffix
1246 // The prefix calls a new template T, which contains all of the suffix:
1247 // F=PREFIX~SUFFIX => F=PREFIX~C(T) and T=PARAMS~SUFFIX
1248 private void Refactor(XslNode parent, int split) {
1249 Debug.Assert(split > 0);
1250 var content = (List<XslNode>)parent.Content;
1252 var node = content[split];
1254 // Generate unique name for the new template
1255 QilName templatename = AstFactory.QName("generated", compiler.CreatePhantomNamespace(), "compiler");
1257 // Create fake ContextInfo for the new nodes, based on the context for the old node
1258 var fakeCtxInfo = new XsltInput.ContextInfo(node.SourceLine);
1260 // Create the new call-template node
1261 var calltemplate = AstFactory.CallTemplate(templatename, fakeCtxInfo);
1262 XsltLoader.SetInfo(calltemplate, null, fakeCtxInfo);
1264 // Create a new template node
1265 Template newtemplate = AstFactory.Template(templatename, null, XsltLoader.nullMode, double.NaN, node.XslVersion);
1266 XsltLoader.SetInfo(newtemplate, null, fakeCtxInfo);
1267 newTemplates.Push(newtemplate);
1269 // Pre-allocate the new content list to minimize the number of resizes (adding some space for any params)
1270 newtemplate.SetContent(new List<XslNode>(content.Count - split + 8));
1272 // Pass parameters from the current scope into the called template
1273 foreach (var scoperecord in scope.GetActiveRecords()) {
1275 if (!scoperecord.IsVariable) {
1276 // The scope record is either a namespace declaration or an exclusion namespace
1277 Debug.Assert(scoperecord.IsNamespace || scoperecord.ncName == null);
1278 Debug.Assert(!compiler.IsPhantomNamespace(scoperecord.nsUri));
1279 newtemplate.Namespaces = new NsDecl(newtemplate.Namespaces, scoperecord.ncName, scoperecord.nsUri);
1281 else {
1282 // The scope contains a variable that we must pass into the new template
1283 var variable = scoperecord.value;
1285 // Skip variables generated during errors
1286 if (compiler.IsPhantomNamespace(variable.Name.NamespaceUri)) {
1287 continue;
1290 // Need to create a new QilName (can't reuse the one from the variable, eventhough it's exactly the same)
1291 var paramname = AstFactory.QName(variable.Name.LocalName, variable.Name.NamespaceUri, variable.Name.Prefix);
1293 // For each variable in scope, add xsl:with-param to the xsl:call-template
1294 var withparam = AstFactory.VarPar(XslNodeType.WithParam, paramname, '$' + paramname.QualifiedName, XslVersion.Current);
1295 XsltLoader.SetInfo(withparam, null, fakeCtxInfo);
1296 withparam.Namespaces = variable.Namespaces;
1297 calltemplate.AddContent(withparam);
1299 // For each variable in scope, add xsl:param to the xsl:template
1300 var param = AstFactory.VarPar(XslNodeType.Param, paramname, null, XslVersion.Current);
1301 XsltLoader.SetInfo(param, null, fakeCtxInfo);
1302 param.Namespaces = variable.Namespaces;
1303 newtemplate.AddContent(param);
1307 // Move all the other children to the new template as well (AddRange)
1308 for (int t = split; t < content.Count; ++t) {
1309 newtemplate.AddContent(content[t]);
1312 // Replace the child with the rewritten child; remove the rest
1313 content[split] = calltemplate;
1314 content.RemoveRange(split + 1, content.Count - split - 1);
1316 Debug.Assert(parent.Content.Count == split + 1);