//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Dispatcher { using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; using System.Threading; class ProcessingContext { internal ProcessingContext next; // for chaining together in free lists int nodeCount; QueryProcessor processor; EvalStack sequenceStack; EvalStack valueStack; internal ProcessingContext() { this.valueStack = new EvalStack(2, 4); this.sequenceStack = new EvalStack(1, 2); this.nodeCount = -1; } #if NO internal int FrameCount { get { return this.valueStack.FrameCount; } } internal int FramePtr { get { return this.valueStack.FramePtr; } } #endif internal StackFrame this[int frameIndex] { get { return this.valueStack[frameIndex]; } } internal int IterationCount { get { if (-1 == this.nodeCount) { this.nodeCount = this.sequenceStack.CalculateNodecount(); if (this.nodeCount == 0 && !this.sequenceStack.InUse) { this.nodeCount = 1; } } return this.nodeCount; } } internal int NodeCount { get { return this.nodeCount; } set { this.nodeCount = value; } } internal ProcessingContext Next { get { return this.next; } set { this.next = value; } } internal QueryProcessor Processor { get { return this.processor; } set { this.processor = value; } } internal StackFrame SecondArg { get { return this.valueStack.SecondArg; } } internal Value[] Sequences { get { return this.sequenceStack.Buffer; } } internal bool SequenceStackInUse { get { return this.sequenceStack.InUse; } } internal bool StacksInUse { get { return (this.valueStack.frames.Count > 0 || this.sequenceStack.frames.Count > 0); } } internal StackFrame TopArg { get { return this.valueStack.TopArg; } } internal StackFrame TopSequenceArg { get { return this.sequenceStack.TopArg; } } internal Value[] Values { get { return this.valueStack.Buffer; } } internal ProcessingContext Clone() { return this.processor.CloneContext(this); } internal void ClearContext() { this.sequenceStack.Clear(); this.valueStack.Clear(); this.nodeCount = -1; } internal void CopyFrom(ProcessingContext context) { Fx.Assert(null != context, ""); this.processor = context.processor; if (context.sequenceStack.frames.Count > 0) { this.sequenceStack.CopyFrom(ref context.sequenceStack); } else { this.sequenceStack.Clear(); } if (context.valueStack.frames.Count > 0) { this.valueStack.CopyFrom(ref context.valueStack); } else { this.valueStack.Clear(); } this.nodeCount = context.nodeCount; } internal NodeSequence CreateSequence() { NodeSequence sequence = this.processor.PopSequence(); if (null == sequence) { sequence = new NodeSequence(); } sequence.OwnerContext = this; sequence.refCount++; return sequence; } internal bool LoadVariable(int var) { return this.Processor.LoadVariable(this, var); } internal void EvalCodeBlock(Opcode block) { this.processor.Eval(block, this); } internal bool PeekBoolean(int index) { return this.valueStack.PeekBoolean(index); } internal double PeekDouble(int index) { return this.valueStack.PeekDouble(index); } #if NO internal int PeekInteger(int index) { return (int) this.valueStack.PeekInteger(index); } #endif internal NodeSequence PeekSequence(int index) { return this.valueStack.PeekSequence(index); } internal string PeekString(int index) { return this.valueStack.PeekString(index); } internal void PopFrame() { this.valueStack.PopFrame(this); } internal void PopSequenceFrame() { this.sequenceStack.PopFrame(this); this.nodeCount = -1; } internal void PopContextSequenceFrame() { PopSequenceFrame(); if (!this.sequenceStack.InUse) this.sequenceStack.contextOnTopOfStack = false; } internal void Push(bool boolVal) { this.valueStack.Push(boolVal); } internal void Push(bool boolVal, int addCount) { this.valueStack.Push(boolVal, addCount); } #if NO internal void Push(double doubleVal) { this.valueStack.Push(doubleVal); } #endif internal void Push(double doubleVal, int addCount) { this.valueStack.Push(doubleVal, addCount); } internal void Push(NodeSequence sequence) { this.valueStack.Push(sequence); } internal void Push(NodeSequence sequence, int addCount) { this.valueStack.Push(sequence, addCount); } internal void Push(string stringVal) { this.valueStack.Push(stringVal); } internal void Push(string stringVal, int addCount) { this.valueStack.Push(stringVal, addCount); } internal void PushFrame() { this.valueStack.PushFrame(); } internal void PopSequenceFrameToValueStack() { this.sequenceStack.PopSequenceFrameTo(ref this.valueStack); this.nodeCount = -1; } internal void PushSequence(NodeSequence seq) { this.sequenceStack.Push(seq); this.nodeCount = -1; } internal void PushSequenceFrame() { this.sequenceStack.PushFrame(); this.nodeCount = -1; } internal void PushContextSequenceFrame() { if (!this.sequenceStack.InUse) this.sequenceStack.contextOnTopOfStack = true; PushSequenceFrame(); } internal void PushSequenceFrameFromValueStack() { this.valueStack.PopSequenceFrameTo(ref this.sequenceStack); this.nodeCount = -1; } internal void ReleaseSequence(NodeSequence sequence) { Fx.Assert(null != sequence, ""); if (this == sequence.OwnerContext) { sequence.refCount--; Fx.Assert(sequence.refCount >= 0, ""); if (0 == sequence.refCount) { this.processor.ReleaseSequenceToPool(sequence); } } } internal void Release() { this.processor.ReleaseContext(this); } internal void ReplaceSequenceAt(int index, NodeSequence sequence) { this.sequenceStack.ReplaceAt(index, sequence); this.nodeCount = -1; } internal void SaveVariable(int var, int count) { this.Processor.SaveVariable(this, var, count); } internal void SetValue(ProcessingContext context, int index, bool val) { this.valueStack.SetValue(this, index, val); } internal void SetValue(ProcessingContext context, int index, double val) { this.valueStack.SetValue(this, index, val); } internal void SetValue(ProcessingContext context, int index, string val) { this.valueStack.SetValue(this, index, val); } internal void SetValue(ProcessingContext context, int index, NodeSequence val) { this.valueStack.SetValue(this, index, val); } internal void TransferSequenceSize() { this.sequenceStack.TransferSequenceSizeTo(ref this.valueStack); } internal void TransferSequencePositions() { this.sequenceStack.TransferPositionsTo(ref this.valueStack); } #region IQueryBufferPool Members #if NO public virtual void Reset() { this.valueStack.Clear(); this.valueStack.Trim(); this.sequenceStack.Clear(); this.sequenceStack.Trim(); } public virtual void Trim() { this.valueStack.Trim(); this.sequenceStack.Trim(); } #endif #endregion } internal enum QueryProcessingFlags : byte { None = 0x00, Match = 0x01, Message = 0x02, //Select = 0x04, } internal class QueryProcessor : ProcessingContext { SeekableXPathNavigator contextNode; // original context node off which everything started ProcessingContext contextPool; INodeCounter counter; QueryProcessingFlags flags; QueryMatcher matcher; Message message; bool matchMessageBody; int refCount; bool result; // for singleton matches... XPathResult queryResult; // for singleton queries... QueryBranchResultSet resultPool; Collection matchList; ICollection matchSet; // for inverse queries that produce multiple matches ICollection> resultSet; // for inverse queries that produce multiple query results NodeSequence sequencePool; //string selectResults; SubExprVariable[] subExprVars; string messageAction; //string messageAddress; //string messageVia; string messageId; string messageSoapUri; string messageTo; internal QueryProcessor(QueryMatcher matcher) : base() { this.Processor = this; this.matcher = matcher; this.flags = QueryProcessingFlags.Match; // PERF, Microsoft, see if we can just let these to their default init this.messageAction = null; //this.messageAddress = null; //this.messageVia = null; this.messageId = null; this.messageSoapUri = null; this.messageTo = null; if (matcher.SubExprVarCount > 0) { this.subExprVars = new SubExprVariable[matcher.SubExprVarCount]; } } internal string Action { get { return this.messageAction; } set { this.messageAction = value; } } #if NO internal string Address { get { return this.messageAddress; } set { this.messageAddress = value; } } #endif // IMPORTANT: Either ContextNode.get or CounterMarker.get MUST be called before this.counter // can be considered valid. internal SeekableXPathNavigator ContextNode { get { if (null == this.contextNode) { if (null != this.message) { this.contextNode = this.matcher.CreateMessageNavigator(this.message, this.matchMessageBody); } else { #pragma warning suppress 56503 // Microsoft, property is more readable for this throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected)); } this.counter = this.contextNode as INodeCounter; if (null == this.counter) { this.counter = DummyNodeCounter.Dummy; } } return this.contextNode; } set { this.contextNode = value; this.counter = value as INodeCounter; } } internal Message ContextMessage { get { return this.message; } set { this.message = value; if (null != value) { this.flags |= QueryProcessingFlags.Message; } else { this.flags &= ~QueryProcessingFlags.Message; } } } // IMPORTANT: Either ContextNode.get or CounterMarker.get MUST be called before this.counter // can be considered valid. internal int CounterMarker { get { if (this.counter == null) { this.counter = this.ContextNode as INodeCounter; if (this.counter == null) this.counter = DummyNodeCounter.Dummy; } return this.counter.CounterMarker; } set { this.counter.CounterMarker = value; } } #if NO internal QueryProcessingFlags Flags { get { return this.flags; } } internal bool HasContextNode { get { return (null != this.contextNode); } } #endif internal bool MatchBody { #if NO get { return this.matchMessageBody; } #endif set { this.matchMessageBody = value; } } internal QueryMatcher Matcher { get { return this.matcher; } } internal ICollection> ResultSet { get { return this.resultSet; } set { this.resultSet = value; } } internal string MessageId { get { return this.messageId; } set { this.messageId = value; } } internal bool Result { get { return this.result; } set { this.result = value; } } internal XPathResult QueryResult { get { return this.queryResult; } set { this.queryResult = value; } } internal Collection MatchList { get { return this.matchList; } } internal ICollection MatchSet { get { return this.matchSet; } set { this.matchSet = value; } } internal string SoapUri { get { return this.messageSoapUri; } set { this.messageSoapUri = value; } } internal string ToHeader { get { return this.messageTo; } set { this.messageTo = value; } } #if NO internal string Via { get { return this.messageVia; } set { this.messageVia = value; } } #endif internal void AddRef() { Interlocked.Increment(ref this.refCount); } internal void ClearProcessor() { base.ClearContext(); this.flags = QueryProcessingFlags.Match; this.messageAction = null; //this.messageAddress = null; //this.messageVia = null; this.messageId = null; this.messageSoapUri = null; this.messageTo = null; int exprCount = this.matcher.SubExprVarCount; if (exprCount == 0) { // No vars. Recycle entire subexpression cache this.subExprVars = null; return; } SubExprVariable[] vars = this.subExprVars; // save locally for speed if (vars == null) { // Allocate space for sub-expressions this.subExprVars = new SubExprVariable[exprCount]; return; } int varCount = vars.Length; // The # of subexpressions changed since this processor was last used. if (varCount != exprCount) { this.subExprVars = new SubExprVariable[exprCount]; return; } if (varCount == 1) { NodeSequence seq = vars[0].seq; if (seq != null) { this.ReleaseSequenceToPool(seq); } return; } // We can reuse the sub-expression cache // Clear out the sub-expression results from an earlier run, and return sequences to pool for (int i = 0; i < varCount; ++i) { NodeSequence seq = vars[i].seq; if (seq != null && seq.refCount > 0) { this.ReleaseSequenceToPool(seq); } } Array.Clear(vars, 0, vars.Length); } internal ProcessingContext CloneContext(ProcessingContext srcContext) { ProcessingContext context = this.PopContext(); if (null == context) { context = new ProcessingContext(); } context.CopyFrom(srcContext); return context; } internal QueryBranchResultSet CreateResultSet() { QueryBranchResultSet resultSet = this.PopResultSet(); if (null == resultSet) { resultSet = new QueryBranchResultSet(); } else { resultSet.Clear(); } return resultSet; } internal int ElapsedCount(int marker) { return this.counter.ElapsedCount(marker); } internal void EnsureFilterCollection() { this.resultSet = null; if (null == this.matchSet) { if (null == this.matchList) { this.matchList = new Collection(); } else { this.matchList.Clear(); } this.matchSet = this.matchList; } } internal void Eval(Opcode block) { Opcode op = block; try { // Walk over and evaulate the entire trace while (null != op) { op = op.Eval(this); } } catch (XPathNavigatorException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(op)); } catch (NavigatorInvalidBodyAccessException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(op)); } } internal void Eval(Opcode block, ProcessingContext context) { Opcode op = block; try { // Walk over and evaulate the entire trace while (null != op) { op = op.Eval(context); } } catch (XPathNavigatorException e) { throw TraceUtility.ThrowHelperError(e.Process(op), this.message); } catch (NavigatorInvalidBodyAccessException e) { throw TraceUtility.ThrowHelperError(e.Process(op), this.message); } } // // Set up the query processor to match messages // internal void Eval(Opcode block, Message message, bool matchBody) { this.result = false; this.ContextNode = null; this.ContextMessage = message; this.MatchBody = matchBody; this.Eval(block); this.message = null; this.contextNode = null; } internal void Eval(Opcode block, SeekableXPathNavigator navigator) { this.result = false; this.ContextNode = navigator; this.ContextMessage = null; this.Eval(block); } internal bool LoadVariable(ProcessingContext context, int var) { if (this.subExprVars[var].seq == null) { return false; } int iter = context.IterationCount; this.counter.IncreaseBy(iter * this.subExprVars[var].count); NodeSequence seq = this.subExprVars[var].seq; context.PushSequenceFrame(); for (int i = 0; i < iter; ++i) { seq.refCount++; context.PushSequence(seq); } return true; } internal ProcessingContext PopContext() { ProcessingContext context = this.contextPool; if (null != context) { this.contextPool = context.Next; context.Next = null; } return context; } internal NodeSequence PopSequence() { NodeSequence sequence = this.sequencePool; if (null != sequence) { this.sequencePool = sequence.Next; sequence.Next = null; } return sequence; } internal QueryBranchResultSet PopResultSet() { QueryBranchResultSet resultSet = this.resultPool; if (null != resultSet) { this.resultPool = resultSet.Next; resultSet.Next = null; } return resultSet; } internal void PushContext(ProcessingContext context) { Fx.Assert(null != context, ""); context.Next = this.contextPool; this.contextPool = context; } internal void PushResultSet(QueryBranchResultSet resultSet) { Fx.Assert(null != resultSet, ""); resultSet.Next = this.resultPool; this.resultPool = resultSet; } internal bool ReleaseRef() { return (Interlocked.Decrement(ref this.refCount) == 0); } internal void ReleaseContext(ProcessingContext context) { Fx.Assert(null != context, ""); this.PushContext(context); } internal void ReleaseResults(QueryBranchResultSet resultSet) { Fx.Assert(null != resultSet, ""); this.PushResultSet(resultSet); } internal void ReleaseSequenceToPool(NodeSequence sequence) { if (NodeSequence.Empty != sequence) { sequence.Reset(this.sequencePool); this.sequencePool = sequence; } } internal void SaveVariable(ProcessingContext context, int var, int count) { NodeSequence seq = context.Sequences[context.TopSequenceArg.basePtr].Sequence; if (seq == null) seq = CreateSequence(); seq.OwnerContext = null; this.subExprVars[var].seq = seq; this.subExprVars[var].count = count; } #region IQueryBufferPool Members #if NO public override void Reset() { base.Release(); // Trim local pools by releasing all references while (null != this.PopResultSet()); this.resultPool = null; while (null != this.PopSequence()); this.sequencePool = null; while (null != this.PopContext()); this.contextPool = null; } public override void Trim() { // Trim stacks base.Trim(); // Trim local pools individually QueryBranchResultSet result = this.resultPool; while (null != result) { result.Trim(); result = result.Next; } NodeSequence sequence = this.sequencePool; while (null != sequencePool) { sequencePool.Trim(); sequence = sequence.Next; } ProcessingContext context = this.contextPool; while (null != context) { context.Trim(); context = context.Next; } } #endif #endregion struct SubExprVariable { internal NodeSequence seq; internal int count; } } #if NO internal struct QueryStatistics { int backupCapacity; int nodeCapacity; int seqStackCapacity; int seqFrameCapacity; int valFrameCapacity; int valStackCapacity; internal QueryStatistics() { this.backupCapacity = 8; this.nodeCapacity = 8; this.seqFrameCapacity = 2; this.seqStackCapacity = 4; this.valFrameCapacity = 2; this.valStackCapacity = 4; } internal int BackupCapacity { get { return this.backupCapacity; } } internal int NodeCapacity { get { return this.nodeCapacity; } } internal int SeqFrameCapacity { get { return this.seqFrameCapacity; } } internal int SeqStackCapacity { get { return this.seqStackCapacity; } } internal int ValFrameCapacity { get { return this.valFrameCapacity; } } internal int ValStackCapacity { get { return this.valStackCapacity; } } } #endif }