|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml.XPath;
using System.Xml.Xsl.XsltOld.Debugger;
using MS.Internal.Xml.XPath;
namespace System.Xml.Xsl.XsltOld
{
internal sealed class Processor : IXsltProcessor
{
//
// Static constants
//
private const int StackIncrement = 10;
//
// Execution result
//
internal enum ExecResult
{
Continue, // Continues next iteration immediately
Interrupt, // Returns to caller, was processed enough
Done // Execution finished
}
internal enum OutputResult
{
Continue,
Interrupt,
Overflow,
Error,
Ignore
}
private ExecResult _execResult;
//
// Compiled stylesheet
//
private readonly Stylesheet _stylesheet; // Root of import tree of template managers
private readonly RootAction? _rootAction;
private readonly Key[]? _keyList;
private readonly List<TheQuery> _queryStore;
//
// Document Being transformed
//
private readonly XPathNavigator _document;
//
// Execution action stack
//
private readonly HWStack _actionStack;
private readonly HWStack? _debuggerStack;
//
// Register for returning value from calling nested action
//
private StringBuilder? _sharedStringBuilder;
//
// Output related member variables
//
private int _ignoreLevel;
private readonly StateMachine _xsm;
private RecordBuilder? _builder;
private XsltOutput _output;
private readonly XmlNameTable _nameTable = new NameTable();
private readonly XmlResolver _resolver;
#pragma warning disable 618
private readonly XsltArgumentList _args;
#pragma warning restore 618
private readonly Hashtable _scriptExtensions;
private ArrayList? _numberList;
//
// Template lookup action
//
private readonly TemplateLookupAction _templateLookup = new TemplateLookupAction();
private readonly IXsltDebugger? _debugger;
private readonly Query[] _queryList;
private ArrayList? _sortArray;
private Hashtable? _documentCache;
// NOTE: ValueOf() can call Matches() through XsltCompileContext.PreserveWhitespace(),
// that's why we use two different contexts here, valueOfContext and matchesContext
private XsltCompileContext? _valueOfContext;
private XsltCompileContext? _matchesContext;
internal XPathNavigator? Current
{
get
{
ActionFrame? frame = (ActionFrame?)_actionStack.Peek();
return frame?.Node;
}
}
internal ExecResult ExecutionResult
{
get { return _execResult; }
set
{
Debug.Assert(_execResult == ExecResult.Continue);
_execResult = value;
}
}
internal Stylesheet Stylesheet
{
get { return _stylesheet; }
}
internal XmlResolver Resolver
{
get
{
Debug.Assert(_resolver != null, "Constructor should create it if null passed");
return _resolver;
}
}
internal ArrayList SortArray
{
get
{
Debug.Assert(_sortArray != null, "InitSortArray() wasn't called");
return _sortArray;
}
}
internal Key[]? KeyList
{
get { return _keyList; }
}
internal XPathNavigator GetNavigator(Uri ruri)
{
XPathNavigator? result;
if (_documentCache != null)
{
result = _documentCache[ruri] as XPathNavigator;
if (result != null)
{
return result.Clone();
}
}
else
{
_documentCache = new Hashtable();
}
object? input = _resolver.GetEntity(ruri, null, null);
if (input is Stream)
{
XmlTextReaderImpl tr = new XmlTextReaderImpl(ruri.ToString(), (Stream)input);
{
tr.XmlResolver = _resolver;
}
// reader is closed by Compiler.LoadDocument()
result = ((IXPathNavigable)Compiler.LoadDocument(tr)).CreateNavigator();
}
else if (input is XPathNavigator)
{
result = (XPathNavigator)input;
}
else
{
throw XsltException.Create(SR.Xslt_CantResolve, ruri.ToString());
}
_documentCache[ruri] = result!.Clone();
return result;
}
internal void AddSort(Sort sortinfo)
{
Debug.Assert(_sortArray != null, "InitSortArray() wasn't called");
_sortArray.Add(sortinfo);
}
internal void InitSortArray()
{
if (_sortArray == null)
{
_sortArray = new ArrayList();
}
else
{
_sortArray.Clear();
}
}
internal object? GetGlobalParameter(XmlQualifiedName qname)
{
object? parameter = _args.GetParam(qname.Name, qname.Namespace);
if (parameter == null)
{
return null;
}
if (
parameter is XPathNodeIterator ||
parameter is XPathNavigator ||
parameter is bool ||
parameter is double ||
parameter is string
)
{
// doing nothing
}
else if (
parameter is short || parameter is ushort ||
parameter is int || parameter is uint ||
parameter is long || parameter is ulong ||
parameter is float || parameter is decimal
)
{
parameter = XmlConvert.ToXPathDouble(parameter);
}
else
{
parameter = parameter.ToString();
}
return parameter;
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = XsltArgumentList.ExtensionObjectSuppresion)]
internal object? GetExtensionObject(string nsUri)
{
return _args.GetExtensionObject(nsUri);
}
internal object? GetScriptObject(string nsUri)
{
return _scriptExtensions[nsUri];
}
internal RootAction? RootAction
{
get { return _rootAction; }
}
internal XPathNavigator Document
{
get { return _document; }
}
#if DEBUG
private bool _stringBuilderLocked;
#endif
internal StringBuilder GetSharedStringBuilder()
{
#if DEBUG
Debug.Assert(!_stringBuilderLocked);
#endif
if (_sharedStringBuilder == null)
{
_sharedStringBuilder = new StringBuilder();
}
else
{
_sharedStringBuilder.Length = 0;
}
#if DEBUG
_stringBuilderLocked = true;
#endif
return _sharedStringBuilder;
}
#pragma warning disable CA1822
internal void ReleaseSharedStringBuilder()
{
// don't clean stringBuilderLocked here. ToString() will happen after this call
#if DEBUG
_stringBuilderLocked = false;
#endif
}
#pragma warning restore CA1822
internal ArrayList NumberList => _numberList ??= new ArrayList();
internal IXsltDebugger? Debugger
{
get { return _debugger; }
}
internal HWStack ActionStack
{
get { return _actionStack; }
}
internal XsltOutput Output
{
get { return _output; }
}
//
// Construction
//
public Processor(
XPathNavigator doc, XsltArgumentList? args, XmlResolver? resolver,
Stylesheet stylesheet, List<TheQuery> queryStore, RootAction rootAction,
IXsltDebugger? debugger
)
{
_stylesheet = stylesheet;
_queryStore = queryStore;
_rootAction = rootAction;
_queryList = new Query[queryStore.Count];
{
for (int i = 0; i < queryStore.Count; i++)
{
_queryList[i] = Query.Clone(queryStore[i].CompiledQuery.QueryTree);
}
}
_xsm = new StateMachine();
_document = doc;
_builder = null;
_actionStack = new HWStack(StackIncrement);
_output = _rootAction.Output;
_resolver = resolver ?? XmlResolver.ThrowingResolver;
_args = args ?? new XsltArgumentList();
_debugger = debugger;
if (_debugger != null)
{
_debuggerStack = new HWStack(StackIncrement, /*limit:*/1000);
_templateLookup = new TemplateLookupActionDbg();
}
// Clone the compile-time KeyList
if (_rootAction.KeyList != null)
{
_keyList = new Key[_rootAction.KeyList.Count];
for (int i = 0; i < _keyList.Length; i++)
{
_keyList[i] = _rootAction.KeyList[i].Clone();
}
}
_scriptExtensions = new Hashtable(_stylesheet.ScriptObjectTypes.Count);
{
// Scripts are not supported on stylesheets
if (_stylesheet.ScriptObjectTypes.Count > 0)
{
throw new PlatformNotSupportedException(SR.CompilingScriptsNotSupported);
}
}
this.PushActionFrame(_rootAction, /*nodeSet:*/null);
}
public ReaderOutput StartReader()
{
ReaderOutput output = new ReaderOutput(this);
_builder = new RecordBuilder(output, _nameTable);
return output;
}
public void Execute(Stream stream)
{
IRecordOutput? recOutput = null;
switch (_output.Method)
{
case XsltOutput.OutputMethod.Text:
recOutput = new TextOnlyOutput(this, stream);
break;
case XsltOutput.OutputMethod.Xml:
case XsltOutput.OutputMethod.Html:
case XsltOutput.OutputMethod.Other:
case XsltOutput.OutputMethod.Unknown:
recOutput = new TextOutput(this, stream);
break;
}
_builder = new RecordBuilder(recOutput!, _nameTable);
Execute();
}
public void Execute(TextWriter writer)
{
IRecordOutput? recOutput = null;
switch (_output.Method)
{
case XsltOutput.OutputMethod.Text:
recOutput = new TextOnlyOutput(this, writer);
break;
case XsltOutput.OutputMethod.Xml:
case XsltOutput.OutputMethod.Html:
case XsltOutput.OutputMethod.Other:
case XsltOutput.OutputMethod.Unknown:
recOutput = new TextOutput(this, writer);
break;
}
_builder = new RecordBuilder(recOutput!, _nameTable);
Execute();
}
public void Execute(XmlWriter writer)
{
_builder = new RecordBuilder(new WriterOutput(this, writer), _nameTable);
Execute();
}
//
// Execution part of processor
//
internal void Execute()
{
Debug.Assert(_actionStack != null);
while (_execResult == ExecResult.Continue)
{
ActionFrame? frame = (ActionFrame?)_actionStack.Peek();
if (frame == null)
{
Debug.Assert(_builder != null);
_builder.TheEnd();
ExecutionResult = ExecResult.Done;
break;
}
// Execute the action which was on the top of the stack
if (frame.Execute(this))
{
_actionStack.Pop();
}
}
if (_execResult == ExecResult.Interrupt)
{
_execResult = ExecResult.Continue;
}
}
//
// Action frame support
//
internal ActionFrame PushNewFrame()
{
ActionFrame? prent = (ActionFrame?)_actionStack.Peek();
ActionFrame frame = (ActionFrame)_actionStack.Push();
if (frame == null)
{
frame = new ActionFrame();
_actionStack.AddToTop(frame);
}
Debug.Assert(frame != null);
if (prent != null)
{
frame.Inherit(prent);
}
return frame;
}
internal void PushActionFrame(Action action, XPathNodeIterator? nodeSet)
{
ActionFrame frame = PushNewFrame();
frame.Init(action, nodeSet);
}
internal void PushActionFrame(ActionFrame container)
{
this.PushActionFrame(container, container.NodeSet);
}
internal void PushActionFrame(ActionFrame container, XPathNodeIterator? nodeSet)
{
ActionFrame frame = PushNewFrame();
frame.Init(container, nodeSet);
}
internal void PushTemplateLookup(XPathNodeIterator? nodeSet, XmlQualifiedName? mode, Stylesheet? importsOf)
{
Debug.Assert(_templateLookup != null);
_templateLookup.Initialize(mode, importsOf);
PushActionFrame(_templateLookup, nodeSet);
}
internal string GetQueryExpression(int key)
{
Debug.Assert(key != Compiler.InvalidQueryKey);
return _queryStore[key].CompiledQuery.Expression;
}
internal Query GetCompiledQuery(int key)
{
Debug.Assert(key != Compiler.InvalidQueryKey);
TheQuery theQuery = _queryStore[key];
theQuery.CompiledQuery.CheckErrors();
Query expr = Query.Clone(_queryList[key]);
expr.SetXsltContext(new XsltCompileContext(theQuery._ScopeManager, this));
return expr;
}
internal Query GetValueQuery(int key)
{
return GetValueQuery(key, null);
}
internal Query GetValueQuery(int key, XsltCompileContext? context)
{
Debug.Assert(key != Compiler.InvalidQueryKey);
TheQuery theQuery = _queryStore[key];
theQuery.CompiledQuery.CheckErrors();
Query expr = _queryList[key];
if (context == null)
{
context = new XsltCompileContext(theQuery._ScopeManager, this);
}
else
{
context.Reinitialize(theQuery._ScopeManager, this);
}
expr.SetXsltContext(context);
return expr;
}
private XsltCompileContext GetValueOfContext() =>
_valueOfContext ??= new XsltCompileContext();
[Conditional("DEBUG")]
private void RecycleValueOfContext()
{
_valueOfContext?.Recycle();
}
private XsltCompileContext GetMatchesContext() => _matchesContext ??= new XsltCompileContext();
[Conditional("DEBUG")]
private void RecycleMatchesContext()
{
_matchesContext?.Recycle();
}
internal string? ValueOf(ActionFrame context, int key)
{
string? result;
Query query = this.GetValueQuery(key, GetValueOfContext());
object value = query.Evaluate(context.NodeSet!);
if (value is XPathNodeIterator)
{
XPathNavigator? n = query.Advance();
result = n != null ? ValueOf(n) : string.Empty;
}
else
{
result = XmlConvert.ToXPathString(value);
}
RecycleValueOfContext();
return result;
}
internal string ValueOf(XPathNavigator n)
{
if (_stylesheet.Whitespace && n.NodeType == XPathNodeType.Element)
{
StringBuilder builder = this.GetSharedStringBuilder();
ElementValueWithoutWS(n, builder);
this.ReleaseSharedStringBuilder();
return builder.ToString();
}
return n.Value;
}
private void ElementValueWithoutWS(XPathNavigator nav, StringBuilder builder)
{
Debug.Assert(nav.NodeType == XPathNodeType.Element);
bool preserve = this.Stylesheet.PreserveWhiteSpace(this, nav);
if (nav.MoveToFirstChild())
{
do
{
switch (nav.NodeType)
{
case XPathNodeType.Text:
case XPathNodeType.SignificantWhitespace:
builder.Append(nav.Value);
break;
case XPathNodeType.Whitespace:
if (preserve)
{
builder.Append(nav.Value);
}
break;
case XPathNodeType.Element:
ElementValueWithoutWS(nav, builder);
break;
}
} while (nav.MoveToNext());
nav.MoveToParent();
}
}
internal XPathNodeIterator StartQuery(XPathNodeIterator context, int key)
{
Query query = GetCompiledQuery(key);
object result = query.Evaluate(context);
if (result is XPathNodeIterator)
{
return new XPathSelectionIterator(context.Current!, query);
}
throw XsltException.Create(SR.XPath_NodeSetExpected);
}
internal object Evaluate(ActionFrame context, int key)
{
return GetValueQuery(key).Evaluate(context.NodeSet!);
}
internal object RunQuery(ActionFrame context, int key)
{
Query query = GetCompiledQuery(key);
object value = query.Evaluate(context.NodeSet!);
XPathNodeIterator? it = value as XPathNodeIterator;
if (it != null)
{
return new XPathArrayIterator(it);
}
return value;
}
internal string EvaluateString(ActionFrame context, int key)
{
object objValue = Evaluate(context, key);
string? value = null;
if (objValue != null)
value = XmlConvert.ToXPathString(objValue);
return value ?? string.Empty;
}
internal bool EvaluateBoolean(ActionFrame context, int key)
{
object objValue = Evaluate(context, key);
if (objValue != null)
{
XPathNavigator? nav = objValue as XPathNavigator;
return nav != null ? Convert.ToBoolean(nav.Value, CultureInfo.InvariantCulture) : Convert.ToBoolean(objValue, CultureInfo.InvariantCulture);
}
else
{
return false;
}
}
internal bool Matches(XPathNavigator? context, int key)
{
// We don't use XPathNavigator.Matches() to avoid cloning of Query on each call
Query query = this.GetValueQuery(key, GetMatchesContext());
try
{
bool result = query.MatchNode(context) != null;
RecycleMatchesContext();
return result;
}
catch (XPathException)
{
throw XsltException.Create(SR.Xslt_InvalidPattern, this.GetQueryExpression(key));
}
}
//
// Outputting part of processor
//
internal XmlNameTable NameTable
{
get { return _nameTable; }
}
internal bool CanContinue
{
get { return _execResult == ExecResult.Continue; }
}
internal bool ExecutionDone
{
get { return _execResult == ExecResult.Done; }
}
internal void ResetOutput()
{
Debug.Assert(_builder != null);
_builder.Reset();
}
internal bool BeginEvent(XPathNodeType nodeType, string? prefix, string? name, string? nspace, bool empty)
{
return BeginEvent(nodeType, prefix, name, nspace, empty, null, true);
}
internal bool BeginEvent(XPathNodeType nodeType, string? prefix, string? name, string? nspace, bool empty, object? htmlProps, bool search)
{
Debug.Assert(_xsm != null);
int stateOutlook = _xsm.BeginOutlook(nodeType);
if (_ignoreLevel > 0 || stateOutlook == StateMachine.Error)
{
_ignoreLevel++;
return true; // We consumed the event, so pretend it was output.
}
switch (_builder!.BeginEvent(stateOutlook, nodeType, prefix, name, nspace, empty, htmlProps, search))
{
case OutputResult.Continue:
_xsm.Begin(nodeType);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State);
Debug.Assert(ExecutionResult == ExecResult.Continue);
return true;
case OutputResult.Interrupt:
_xsm.Begin(nodeType);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State);
ExecutionResult = ExecResult.Interrupt;
return true;
case OutputResult.Overflow:
ExecutionResult = ExecResult.Interrupt;
return false;
case OutputResult.Error:
_ignoreLevel++;
return true;
case OutputResult.Ignore:
return true;
default:
Debug.Fail("Unexpected result of RecordBuilder.BeginEvent()");
return true;
}
}
internal bool TextEvent(string? text)
{
return this.TextEvent(text, false);
}
internal bool TextEvent(string? text, bool disableOutputEscaping)
{
Debug.Assert(_xsm != null);
if (_ignoreLevel > 0)
{
return true;
}
int stateOutlook = _xsm.BeginOutlook(XPathNodeType.Text);
switch (_builder!.TextEvent(stateOutlook, text, disableOutputEscaping))
{
case OutputResult.Continue:
_xsm.Begin(XPathNodeType.Text);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State);
Debug.Assert(ExecutionResult == ExecResult.Continue);
return true;
case OutputResult.Interrupt:
_xsm.Begin(XPathNodeType.Text);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State);
ExecutionResult = ExecResult.Interrupt;
return true;
case OutputResult.Overflow:
ExecutionResult = ExecResult.Interrupt;
return false;
case OutputResult.Error:
case OutputResult.Ignore:
return true;
default:
Debug.Fail("Unexpected result of RecordBuilder.TextEvent()");
return true;
}
}
internal bool EndEvent(XPathNodeType nodeType)
{
Debug.Assert(_xsm != null);
if (_ignoreLevel > 0)
{
_ignoreLevel--;
return true;
}
int stateOutlook = _xsm.EndOutlook(nodeType);
switch (_builder!.EndEvent(stateOutlook, nodeType))
{
case OutputResult.Continue:
_xsm.End(nodeType);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State);
return true;
case OutputResult.Interrupt:
_xsm.End(nodeType);
Debug.Assert(StateMachine.StateOnly(stateOutlook) == _xsm.State,
"StateMachine.StateOnly(stateOutlook) == this.xsm.State");
ExecutionResult = ExecResult.Interrupt;
return true;
case OutputResult.Overflow:
ExecutionResult = ExecResult.Interrupt;
return false;
case OutputResult.Error:
case OutputResult.Ignore:
default:
Debug.Fail("Unexpected result of RecordBuilder.TextEvent()");
return true;
}
}
internal bool CopyBeginEvent(XPathNavigator node, bool emptyflag)
{
switch (node.NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Attribute:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
return BeginEvent(node.NodeType, node.Prefix, node.LocalName, node.NamespaceURI, emptyflag);
case XPathNodeType.Namespace:
// value instead of namespace here!
return BeginEvent(XPathNodeType.Namespace, null, node.LocalName, node.Value, false);
case XPathNodeType.Text:
// Text will be copied in CopyContents();
break;
case XPathNodeType.Root:
case XPathNodeType.Whitespace:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.All:
break;
default:
Debug.Fail("Invalid XPathNodeType in CopyBeginEvent");
break;
}
return true;
}
internal bool CopyTextEvent(XPathNavigator node)
{
switch (node.NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Namespace:
break;
case XPathNodeType.Attribute:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
case XPathNodeType.Text:
case XPathNodeType.Whitespace:
case XPathNodeType.SignificantWhitespace:
string text = node.Value;
return TextEvent(text);
case XPathNodeType.Root:
case XPathNodeType.All:
break;
default:
Debug.Fail("Invalid XPathNodeType in CopyTextEvent");
break;
}
return true;
}
internal bool CopyEndEvent(XPathNavigator node)
{
switch (node.NodeType)
{
case XPathNodeType.Element:
case XPathNodeType.Attribute:
case XPathNodeType.ProcessingInstruction:
case XPathNodeType.Comment:
case XPathNodeType.Namespace:
return EndEvent(node.NodeType);
case XPathNodeType.Text:
// Text was copied in CopyContents();
break;
case XPathNodeType.Root:
case XPathNodeType.Whitespace:
case XPathNodeType.SignificantWhitespace:
case XPathNodeType.All:
break;
default:
Debug.Fail("Invalid XPathNodeType in CopyEndEvent");
break;
}
return true;
}
internal static bool IsRoot(XPathNavigator navigator)
{
Debug.Assert(navigator != null);
if (navigator.NodeType == XPathNodeType.Root)
{
return true;
}
else if (navigator.NodeType == XPathNodeType.Element)
{
XPathNavigator clone = navigator.Clone();
clone.MoveToRoot();
return clone.IsSamePosition(navigator);
}
else
{
return false;
}
}
//
// Builder stack
//
internal void PushOutput(IRecordOutput output)
{
Debug.Assert(output != null);
_builder!.OutputState = _xsm.State;
RecordBuilder lastBuilder = _builder;
_builder = new RecordBuilder(output, _nameTable);
_builder.Next = lastBuilder;
_xsm.Reset();
}
internal IRecordOutput PopOutput()
{
Debug.Assert(_builder != null);
RecordBuilder topBuilder = _builder;
_builder = topBuilder.Next!;
_xsm.State = _builder.OutputState;
topBuilder.TheEnd();
return topBuilder.Output;
}
internal bool SetDefaultOutput(XsltOutput.OutputMethod method)
{
if (Output.Method != method)
{
_output = _output.CreateDerivedOutput(method);
return true;
}
return false;
}
internal object GetVariableValue(VariableAction variable)
{
int variablekey = variable.VarKey;
if (variable.IsGlobal)
{
ActionFrame rootFrame = (ActionFrame)_actionStack[0];
object result = rootFrame.GetVariable(variablekey);
if (result == VariableAction.BeingComputedMark)
{
throw XsltException.Create(SR.Xslt_CircularReference, variable.NameStr);
}
if (result != null)
{
return result;
}
// Variable wasn't evaluated yet
int saveStackSize = _actionStack.Length;
ActionFrame varFrame = PushNewFrame();
varFrame.Inherit(rootFrame);
varFrame.Init(variable, rootFrame.NodeSet);
do
{
bool endOfFrame = ((ActionFrame)_actionStack.Peek()!).Execute(this);
if (endOfFrame)
{
_actionStack.Pop();
}
} while (saveStackSize < _actionStack.Length);
Debug.Assert(saveStackSize == _actionStack.Length);
result = rootFrame.GetVariable(variablekey);
Debug.Assert(result != null, "Variable was just calculated and result can't be null");
return result;
}
else
{
return ((ActionFrame)_actionStack.Peek()!).GetVariable(variablekey);
}
}
internal void SetParameter(XmlQualifiedName name, object value)
{
Debug.Assert(1 < _actionStack.Length);
ActionFrame parentFrame = (ActionFrame)_actionStack[_actionStack.Length - 2];
parentFrame.SetParameter(name, value);
}
internal void ResetParams()
{
ActionFrame frame = (ActionFrame)_actionStack[_actionStack.Length - 1];
frame.ResetParams();
}
internal object? GetParameter(XmlQualifiedName name)
{
Debug.Assert(2 < _actionStack.Length);
ActionFrame parentFrame = (ActionFrame)_actionStack[_actionStack.Length - 3];
return parentFrame.GetParameter(name);
}
// ---------------------- Debugger stack -----------------------
internal sealed class DebuggerFrame
{
internal ActionFrame? actionFrame;
internal XmlQualifiedName? currentMode;
}
internal void PushDebuggerStack()
{
Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
DebuggerFrame dbgFrame = (DebuggerFrame)_debuggerStack!.Push();
if (dbgFrame == null)
{
dbgFrame = new DebuggerFrame();
_debuggerStack.AddToTop(dbgFrame);
}
dbgFrame.actionFrame = (ActionFrame?)_actionStack.Peek(); // In a case of next builtIn action.
}
internal void PopDebuggerStack()
{
Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
_debuggerStack!.Pop();
}
internal void OnInstructionExecute()
{
Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
DebuggerFrame? dbgFrame = (DebuggerFrame?)_debuggerStack!.Peek();
Debug.Assert(dbgFrame != null, "PushDebuggerStack() wasn't ever called");
dbgFrame.actionFrame = (ActionFrame?)_actionStack.Peek();
this.Debugger.OnInstructionExecute((IXsltProcessor)this);
}
internal XmlQualifiedName? GetPreviousMode()
{
Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
Debug.Assert(2 <= _debuggerStack!.Length);
return ((DebuggerFrame)_debuggerStack[_debuggerStack.Length - 2]!).currentMode;
}
internal void SetCurrentMode(XmlQualifiedName? mode)
{
Debug.Assert(this.Debugger != null, "We don't generate calls this function if ! debugger");
((DebuggerFrame)_debuggerStack![_debuggerStack.Length - 1]).currentMode = mode;
}
}
}
|