//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Dispatcher { using System.Collections.Generic; using System.Runtime; using System.Threading; enum OpcodeID { NoOp, SubExpr, // Flow opcodes Branch, JumpIfNot, Filter, Function, XsltFunction, XsltInternalFunction, Cast, QueryTree, BlockEnd, SubRoutine, // Set Opcodes Ordinal, LiteralOrdinal, Empty, Union, Merge, // Boolean opcodes ApplyBoolean, StartBoolean, EndBoolean, // Relational opcodes Relation, StringEquals, NumberEquals, StringEqualsBranch, NumberEqualsBranch, NumberRelation, NumberInterval, NumberIntervalBranch, // Select/Node Operators Select, InitialSelect, SelectRoot, // Stack operators PushXsltVariable, PushBool, PushString, PushDouble, PushContextNode, PushNodeSequence, PushPosition, PopSequenceToValueStack, PopSequenceToSequenceStack, PopContextNodes, PushContextCopy, PopValueFrame, // Math opcode Plus, Minus, Multiply, Divide, Mod, Negate, // Specialized String operators StringPrefix, StringPrefixBranch, // Results MatchAlways, MatchResult, MatchFilterResult, MatchMultipleResult, MatchSingleFx, QuerySingleFx, QueryResult, QueryMultipleResult } enum OpcodeFlags { None = 0x00000000, Single = 0x00000001, Multiple = 0x00000002, Branch = 0x00000004, Result = 0x00000008, Jump = 0x00000010, Literal = 0x00000020, Select = 0x00000040, Deleted = 0x00000080, InConditional = 0x00000100, NoContextCopy = 0x00000200, InitialSelect = 0x00000400, CompressableSelect = 0x00000800, Fx = 0x00001000 } abstract class Opcode { protected OpcodeFlags flags; protected Opcode next; OpcodeID opcodeID; protected Opcode prev; #if DEBUG // debugging aid. Because C# references do not have displayble numeric values, hard to deduce the // graph structure to see what opcode is connected to what static long nextUniqueId = 0; internal long uniqueID; #endif internal Opcode(OpcodeID id) { this.opcodeID = id; this.flags = OpcodeFlags.Single; #if DEBUG this.uniqueID = Opcode.NextUniqueId(); #endif } internal OpcodeFlags Flags { get { return this.flags; } set { this.flags = value; } } internal OpcodeID ID { get { return this.opcodeID; } } internal Opcode Next { get { return this.next; } set { this.next = value; } } internal Opcode Prev { get { return this.prev; } set { this.prev = value; } } #if DEBUG static long NextUniqueId() { return Interlocked.Increment(ref Opcode.nextUniqueId); } #endif internal virtual void Add(Opcode op) { Fx.Assert(null != op, ""); Fx.Assert(null != this.prev, ""); // Create a branch that will include both this and the new opcode this.prev.AddBranch(op); } internal virtual void AddBranch(Opcode opcode) { Fx.Assert(null != opcode, ""); // Replace what follows this opcode with a branch containing .next and the new opcode // If this opcode is a conditional, then since the tree structure is about to change, conditional // reachability for everything that follows is about to change. // 1. Remove .next from the conditional's AlwaysBranch Table. // 2. Create the new branch structure. // 3. The branch, once in the tree, will fix up all conditional jumps Opcode next = this.next; if (this.TestFlag(OpcodeFlags.InConditional)) { this.DelinkFromConditional(next); } BranchOpcode branch = new BranchOpcode(); this.next = null; this.Attach(branch); if (null != next) { Fx.Assert(OpcodeID.Branch != next.ID, ""); branch.Add(next); } branch.Add(opcode); } internal void Attach(Opcode op) { Fx.Assert(null == this.next, ""); this.next = op; op.prev = this; } internal virtual void CollectXPathFilters(ICollection filters) { if (this.next != null) { this.next.CollectXPathFilters(filters); } } internal virtual bool IsEquivalentForAdd(Opcode opcode) { return (this.ID == opcode.ID); } internal bool IsMultipleResult() { return ((this.flags & (OpcodeFlags.Result | OpcodeFlags.Multiple)) == (OpcodeFlags.Result | OpcodeFlags.Multiple)); } internal virtual void DelinkFromConditional(Opcode child) { Fx.Assert(this.TestFlag(OpcodeFlags.InConditional), ""); if (this.TestFlag(OpcodeFlags.InConditional)) { ((QueryConditionalBranchOpcode)this.prev).RemoveAlwaysBranch(child); } } internal Opcode DetachChild() { Opcode child = this.next; if (child != null) { if (this.IsInConditional()) { this.DelinkFromConditional(child); } } this.next = null; child.prev = null; return child; } internal void DetachFromParent() { Opcode parent = this.prev; if (parent != null) { parent.DetachChild(); } } internal virtual bool Equals(Opcode op) { return (op.ID == this.ID); } internal virtual Opcode Eval(ProcessingContext context) { return this.next; } internal virtual Opcode Eval(NodeSequence sequence, SeekableXPathNavigator node) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected)); } internal virtual Opcode EvalSpecial(ProcessingContext context) { return this.Eval(context); } internal virtual bool IsInConditional() { return this.TestFlag(OpcodeFlags.InConditional); } internal bool IsReachableFromConditional() { return (null != this.prev && this.prev.IsInConditional()); } internal virtual Opcode Locate(Opcode opcode) { Fx.Assert(null != opcode, ""); if (null != this.next && this.next.Equals(opcode)) { return this.next; } return null; } internal virtual void LinkToConditional(Opcode child) { Fx.Assert(this.TestFlag(OpcodeFlags.InConditional), ""); if (this.TestFlag(OpcodeFlags.InConditional)) { ((QueryConditionalBranchOpcode)this.prev).AddAlwaysBranch(this, child); } } internal virtual void Remove() { if (null == this.next) { Opcode prevOpcode = this.prev; if (null != prevOpcode) { prevOpcode.RemoveChild(this); prevOpcode.Remove(); } } } internal virtual void RemoveChild(Opcode opcode) { Fx.Assert(null != opcode, ""); Fx.Assert(this.next == opcode, ""); if (this.IsInConditional()) { this.DelinkFromConditional(opcode); } opcode.prev = null; this.next = null; opcode.Flags |= OpcodeFlags.Deleted; } internal virtual void Replace(Opcode replace, Opcode with) { Fx.Assert(null != replace && null != with, ""); if (this.next == replace) { bool isConditional = this.IsInConditional(); if (isConditional) { this.DelinkFromConditional(this.next); } this.next.prev = null; this.next = with; with.prev = this; if (isConditional) { this.LinkToConditional(with); } } } internal bool TestFlag(OpcodeFlags flag) { return (0 != (this.flags & flag)); } #if DEBUG_FILTER public override string ToString() { #if DEBUG return string.Format("{0}(#{1})", this.opcodeID.ToString(), this.uniqueID); #else return this.opcodeID.ToString(); #endif } #endif internal virtual void Trim() { if (this.next != null) { this.next.Trim(); } } } struct OpcodeBlock { Opcode first; Opcode last; internal OpcodeBlock(Opcode first) { this.first = first; this.first.Prev = null; for (this.last = this.first; this.last.Next != null; this.last = this.last.Next); } #if FILTEROPTIMIZER internal OpcodeBlock(Opcode first, Opcode last) { this.first = first; this.first.Prev = null; this.last = last; this.Last.Next = null; } #endif internal Opcode First { get { return this.first; } } internal Opcode Last { get { return this.last; } } internal void Append(Opcode opcode) { Fx.Assert(null != opcode, ""); if (null == this.last) { this.first = opcode; this.last = opcode; } else { this.last.Attach(opcode); opcode.Next = null; this.last = opcode; } } internal void Append(OpcodeBlock block) { if (null == this.last) { this.first = block.first; this.last = block.last; } else { this.last.Attach(block.first); this.last = block.last; } } internal void DetachLast() { if (null == this.last) { return; } Opcode newLast = this.last.Prev; this.last.Prev = null; this.last = newLast; if (null != this.last) { this.last.Next = null; } } } class OpcodeList { QueryBuffer opcodes; public OpcodeList(int capacity) { this.opcodes = new QueryBuffer(capacity); } public int Count { get { return this.opcodes.count; } } public Opcode this[int index] { get { return this.opcodes[index]; } set { this.opcodes[index] = value; } } public void Add(Opcode opcode) { this.opcodes.Add(opcode); } public int IndexOf(Opcode opcode) { return this.opcodes.IndexOf(opcode); } public void Remove(Opcode opcode) { this.opcodes.Remove(opcode); } public void Trim() { this.opcodes.TrimToCount(); } } }