1 //------------------------------------------------------------------------------
2 // <copyright file="XslAstAnalyzer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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
;
27 // List of all variables and parameters
28 private List
<VarPar
> allVarPars
= new List
<VarPar
>();
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
>>();
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> ----------------------------------
53 /// Represents a graph using hashtable of adjacency lists.
55 /// <typeparam name="V">Vertex type</typeparam>
56 internal class Graph
<V
> : Dictionary
<V
, List
<V
>>
59 private static IList
<V
> empty
= (new List
<V
>()).AsReadOnly();
61 public IEnumerable
<V
> GetAdjList(V v
) {
63 if (TryGetValue(v
, out adjList
) && adjList
!= null) {
69 public void AddEdge(V v1
, V v2
) {
71 if ((object)v1
== (object)v2
) {
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
82 if (!TryGetValue(v2
, out adjList
)) {
86 Debug
.WriteLineIf(DiagnosticsSwitches
.XslTypeInference
.TraceVerbose
, v1
.TraceName
+ " -> " + v2
.TraceName
);
89 public void PropagateFlag(XslFlags flag
) {
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
{
120 public ModeName(QilName mode
, QilName 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
) {
146 par
.Flags
|= XslFlags
.AnyType
;
148 foreach (VarPar
var in compiler
.GlobalVars
) {
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
) {
169 if (instr
.NodeType
!= XslNodeType
.Param
) {
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
);
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]);
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
) {
240 if (! parentModeFlags
.TryGetValue(modeFlag
.Key
, out modeFlags
)) {
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) {
251 if (! parentModeFlags
.TryGetValue(tmpl
.Mode
, out modeFlags
)) {
254 parentModeFlags
[tmpl
.Mode
] = modeFlags
| templateFlags
;
259 private void TraceResults() {
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) {
281 if ((tmpl
.Flags
& XslFlags
.Position
) != 0) {
284 if ((tmpl
.Flags
& XslFlags
.Last
) != 0) {
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;
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
330 protected override XslFlags
Visit(XslNode node
) {
331 scope
.EnterScope(node
.Namespaces
);
332 XslFlags result
= base.Visit(node
);
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
);
340 (result
& XslFlags
.TypeFilter
& ~XslFlags
.Rtf
) == 0,
341 "Instructions always return Rtf. node=" + node
.NodeType
.ToString() + " result=" + result
.ToString()
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
);
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
);
362 protected override XslFlags
VisitTemplate(Template node
) {
363 // @match does not affect any flags
364 //ProcessPattern(match);
365 node
.Flags
= VisitChildren(node
);
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
);
387 if (!applyTemplatesParams
.TryGetValue(mn
, out modePar
)) {
388 modePar
= applyTemplatesParams
[mn
] = AstFactory
.WithParam(instr
.Name
);
391 if (typeDonor
!= null) {
392 dataFlow
.AddEdge(typeDonor
, modePar
);
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
) {
409 ProcessAvt(node
.NameAvt
) |
410 ProcessAvt(node
.NsAvt
) |
415 protected override XslFlags
VisitCallTemplate(XslNode node
) {
416 XslFlags result
= XslFlags
.None
;
419 if (!compiler
.NamedTemplates
.TryGetValue(node
.Name
, out target
)) {
420 Debug
.WriteLineIf(DiagnosticsSwitches
.XslTypeInference
.TraceError
, "Unknown template " + node
.Name
.QualifiedName
, "Error");
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
);
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
];
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
) {
454 if (instr
.NodeType
!= XslNodeType
.Param
) {
458 VarPar par
= (VarPar
)instr
;
462 foreach (XslNode withPar
in node
.Content
) {
463 if (withPar
.Name
.Equals(par
.Name
)) {
464 found
= (VarPar
)withPar
;
465 typeDonor
= typeDonors
[idx
];
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
);
477 par
.Flags
|= found
.Flags
& XslFlags
.TypeFilter
;
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
510 ProcessAvt(node
.NameAvt
) |
511 ProcessAvt(node
.NsAvt
) |
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
);
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
--;
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
) {
552 ProcessAvt(node
.Select
) |
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
) {
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
) {
586 ProcessAvt(node
.Select
) |
591 protected override XslFlags
VisitSort(Sort node
) {
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
) {
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
);
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
);
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
) {
663 if (node
.NodeType
!= XslNodeType
.WithParam
) {
664 allVarPars
.Add(node
);
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
;
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
);
685 result
= XslFlags
.String
;
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
);
713 if (templates
[templates
.Count
- 1] == dependentTemplate
) {
714 return; // this is a duplicate
718 templates
.Add(dependentTemplate
);
721 private void PropagateSideEffectsFlag() {
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) {
737 foreach (ProtoTemplate t
in revCall1Graph
.Keys
) {
738 if ((t
.Flags
& XslFlags
.Stop
) == 0) {
739 if ((t
.Flags
& XslFlags
.SideEffects
) != 0) {
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) {
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) {
760 Debug
.Assert((u
.Flags
& XslFlags
.SideEffects
) == XslFlags
.SideEffects
, "Flag was not set on an adjacent vertex");
762 Template template
= t
as Template
;
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) {
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
;
806 public XslFlags
Analyze(string xpathExpr
) {
808 if (xpathExpr
== null) {
809 return XslFlags
.None
;
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
;
820 } catch (XslLoadException
) {
821 return XslFlags
.AnyType
| XslFlags
.FullFocus
;
825 public XslFlags
AnalyzeAvt(string source
) {
827 if (source
== null) {
828 return XslFlags
.None
;
831 xsltCurrentNeeded
= false;
832 XslFlags result
= XslFlags
.None
;
834 while (pos
< source
.Length
) {
835 pos
= source
.IndexOf('{', pos
);
837 break; // no more AVTs
840 if (pos
< source
.Length
&& source
[pos
] == '{') { // "{{"
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
);
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) {
874 return scope
.LookupNamespace(prefix
);
878 public virtual void StartBuild() {
881 public virtual XslFlags
EndBuild(XslFlags result
) {
885 public virtual XslFlags
String(string value) {
887 return XslFlags
.String
;
890 public virtual XslFlags
Number(double value) {
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
) {
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
) {
923 if (xpathAxis
== XPathAxis
.Self
&& nodeType
== XPathNodeType
.All
&& prefix
== null && name
== null) {
924 return XslFlags
.Current
| XslFlags
.Node
;
926 return XslFlags
.Current
| XslFlags
.Nodeset
;
931 public virtual XslFlags
JoinStep(XslFlags left
, XslFlags right
) {
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
) {
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
) {
954 XslFlags argsFlags
= XslFlags
.None
;
955 foreach (XslFlags t
in args
) {
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
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
;
989 string ns
= ResolvePrefix(prefix
);
990 if (ns
== XmlReservedNs
.NsMsxsl
) {
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
) {
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
;
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
,
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
,
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
) {
1107 CheckNodeCost(template
);
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
);
1120 CheckNodeCost(newtemplate
);
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
) {
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
;
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
1219 // The new template (containing the remainder of the current node) will be processed later
1220 nodeCost
-= costForChild
;
1221 nodeCost
+= CallTemplateCost
;
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
);
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
);
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
)) {
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);