//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Dispatcher { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Xml.XPath; internal class SubExpr { internal int var; internal int refCount; // opcodes internal bool useSpecial; Opcode ops; SubExpr parent; protected List children; internal SubExpr(SubExpr parent, Opcode ops, int var) { this.children = new List(2); this.var = var; this.parent = parent; this.useSpecial = false; if (parent != null) { this.ops = new InternalSubExprOpcode(parent); this.ops.Attach(ops); this.useSpecial = parent is SubExprHeader && ((SelectOpcode)ops).Criteria.Axis.Type == QueryAxisType.Child; } else { this.ops = ops; } } internal Opcode FirstOp { get { if (this.parent == null) { return this.ops; } else { return this.ops.Next; } } } internal int Variable { get { return this.var; } } internal SubExprOpcode Add(Opcode opseq, SubExprEliminator elim) { Opcode start = this.FirstOp; Opcode ops = opseq; while (start != null && ops != null && start.Equals(ops)) { start = start.Next; ops = ops.Next; } if (ops == null) { if (start == null) { return new SubExprOpcode(this); } else { SubExpr e = this.BranchAt(start, elim); return new SubExprOpcode(e); } } else { if (start == null) { ops.DetachFromParent(); for (int i = 0; i < this.children.Count; ++i) { if (this.children[i].FirstOp.Equals(ops)) { return this.children[i].Add(ops, elim); } } SubExpr e = new SubExpr(this, ops, elim.NewVarID()); this.AddChild(e); return new SubExprOpcode(e); } else { SubExpr e = this.BranchAt(start, elim); ops.DetachFromParent(); SubExpr ee = new SubExpr(e, ops, elim.NewVarID()); e.AddChild(ee); return new SubExprOpcode(ee); } } } internal virtual void AddChild(SubExpr expr) { this.children.Add(expr); } SubExpr BranchAt(Opcode op, SubExprEliminator elim) { Opcode firstOp = this.FirstOp; if (this.parent != null) { this.parent.RemoveChild(this); } else { elim.Exprs.Remove(this); } firstOp.DetachFromParent(); op.DetachFromParent(); SubExpr e = new SubExpr(this.parent, firstOp, elim.NewVarID()); if (this.parent != null) { this.parent.AddChild(e); } else { elim.Exprs.Add(e); } e.AddChild(this); this.parent = e; this.ops = new InternalSubExprOpcode(e); this.ops.Attach(op); return e; } internal void CleanUp(SubExprEliminator elim) { if (this.refCount == 0) { if (this.children.Count == 0) { if (this.parent == null) { elim.Exprs.Remove(this); } else { this.parent.RemoveChild(this); this.parent.CleanUp(elim); } } else if (this.children.Count == 1) { SubExpr child = this.children[0]; Opcode op = child.FirstOp; op.DetachFromParent(); Opcode op2 = this.ops; while (op2.Next != null) { op2 = op2.Next; } op2.Attach(op); child.ops = this.ops; if (this.parent == null) { elim.Exprs.Remove(this); elim.Exprs.Add(child); child.parent = null; } else { this.parent.RemoveChild(this); this.parent.AddChild(child); child.parent = this.parent; } } } } internal void DecRef(SubExprEliminator elim) { this.refCount--; CleanUp(elim); } internal void Eval(ProcessingContext context) { int count = 0, marker = context.Processor.CounterMarker; Opcode op = this.ops; if (this.useSpecial) { op.EvalSpecial(context); context.LoadVariable(this.var); //context.Processor.CounterMarker = marker; return; } while (op != null) { op = op.Eval(context); } count = context.Processor.ElapsedCount(marker); //context.Processor.CounterMarker = marker; context.SaveVariable(this.var, count); } internal virtual void EvalSpecial(ProcessingContext context) { this.Eval(context); } internal void IncRef() { this.refCount++; } internal virtual void RemoveChild(SubExpr expr) { this.children.Remove(expr); } internal void Renumber(SubExprEliminator elim) { this.var = elim.NewVarID(); for (int i = 0; i < this.children.Count; ++i) { this.children[i].Renumber(elim); } } internal void Trim() { this.children.Capacity = this.children.Count; this.ops.Trim(); for (int i = 0; i < this.children.Count; ++i) { this.children[i].Trim(); } } #if DEBUG_FILTER internal void Write(TextWriter outStream) { outStream.WriteLine("======================="); outStream.WriteLine("= SubExpr #" + this.var.ToString() + " (" + this.refCount.ToString() + ")"); outStream.WriteLine("======================="); for(Opcode o = this.ops; o != null; o = o.Next) { outStream.WriteLine(o.ToString()); } outStream.WriteLine(""); for(int i = 0; i < this.children.Count; ++i) { this.children[i].Write(outStream); } } #endif } internal class SubExprHeader : SubExpr { // WS, Microsoft, Can probably combine these // WS, Microsoft, Make this data structure less ugly (if possible) Dictionary>> nameLookup; Dictionary indexLookup; internal SubExprHeader(Opcode ops, int var) : base(null, ops, var) { this.nameLookup = new Dictionary>>(); this.indexLookup = new Dictionary(); this.IncRef(); // Prevent cleanup } internal override void AddChild(SubExpr expr) { base.AddChild(expr); RebuildIndex(); if (expr.useSpecial) { NodeQName qname = ((SelectOpcode)(expr.FirstOp)).Criteria.QName; string ns = qname.Namespace; Dictionary> nextLookup; if (!this.nameLookup.TryGetValue(ns, out nextLookup)) { nextLookup = new Dictionary>(); this.nameLookup.Add(ns, nextLookup); } string name = qname.Name; List exprs = new List(); if (!nextLookup.TryGetValue(name, out exprs)) { exprs = new List(); nextLookup.Add(name, exprs); } exprs.Add(expr); } } internal override void EvalSpecial(ProcessingContext context) { int marker = context.Processor.CounterMarker; if (!context.LoadVariable(this.var)) { XPathMessageContext.HeaderFun.InvokeInternal(context, 0); context.SaveVariable(this.var, context.Processor.ElapsedCount(marker)); } // WS, Microsoft, see if we can put this array in the processor to save // an allocation. Perhaps we can use the variables slot we're going to fill NodeSequence[] childSequences = new NodeSequence[this.children.Count]; NodeSequence seq = context.Sequences[context.TopSequenceArg.basePtr].Sequence; for (int i = 0; i < this.children.Count; ++i) { childSequences[i] = context.CreateSequence(); childSequences[i].StartNodeset(); } // Perform the index SeekableXPathNavigator nav = seq[0].GetNavigator(); if (nav.MoveToFirstChild()) { do { if (nav.NodeType == XPathNodeType.Element) { List lst; string name = nav.LocalName; string ns = nav.NamespaceURI; Dictionary> nextLookup; if (this.nameLookup.TryGetValue(ns, out nextLookup)) { if (nextLookup.TryGetValue(name, out lst)) { for (int i = 0; i < lst.Count; ++i) { childSequences[this.indexLookup[lst[i]].i].Add(nav); } } if (nextLookup.TryGetValue(QueryDataModel.Wildcard, out lst)) { for (int i = 0; i < lst.Count; ++i) { childSequences[this.indexLookup[lst[i]].i].Add(nav); } } } if (this.nameLookup.TryGetValue(QueryDataModel.Wildcard, out nextLookup)) { if (nextLookup.TryGetValue(QueryDataModel.Wildcard, out lst)) { for (int i = 0; i < lst.Count; ++i) { childSequences[this.indexLookup[lst[i]].i].Add(nav); } } } } } while (nav.MoveToNext()); } int secondMarker = context.Processor.CounterMarker; for (int i = 0; i < this.children.Count; ++i) { if (this.children[i].useSpecial) { childSequences[i].StopNodeset(); context.Processor.CounterMarker = secondMarker; context.PushSequenceFrame(); context.PushSequence(childSequences[i]); Opcode op = this.children[i].FirstOp.Next; while (op != null) { op = op.Eval(context); } context.SaveVariable(this.children[i].var, context.Processor.ElapsedCount(marker)); context.PopSequenceFrame(); } else { context.ReleaseSequence(childSequences[i]); //context.SetVariable(this.children[i].Variable, null, 0); } } context.Processor.CounterMarker = marker; } internal void RebuildIndex() { this.indexLookup.Clear(); for (int i = 0; i < this.children.Count; ++i) { this.indexLookup.Add(this.children[i], new MyInt(i)); } } internal override void RemoveChild(SubExpr expr) { base.RemoveChild(expr); RebuildIndex(); if (expr.useSpecial) { NodeQName qname = ((SelectOpcode)(expr.FirstOp)).Criteria.QName; string ns = qname.Namespace; Dictionary> nextLookup; if (this.nameLookup.TryGetValue(ns, out nextLookup)) { string name = qname.Name; List exprs; if (nextLookup.TryGetValue(name, out exprs)) { exprs.Remove(expr); if (exprs.Count == 0) { nextLookup.Remove(name); } } if (nextLookup.Count == 0) { this.nameLookup.Remove(ns); } } } } internal class MyInt { internal int i; internal MyInt(int i) { this.i = i; } } } internal class SubExprEliminator { List exprList; int nextVar; Dictionary> removalMapping; internal SubExprEliminator() { this.removalMapping = new Dictionary>(); this.exprList = new List(); Opcode op = new XPathMessageFunctionCallOpcode(XPathMessageContext.HeaderFun, 0); SubExprHeader header = new SubExprHeader(op, 0); this.exprList.Add(header); this.nextVar = 1; } internal List Exprs { get { return this.exprList; } } internal int VariableCount { get { return this.nextVar; } } internal Opcode Add(object item, Opcode ops) { List exprs = new List(); this.removalMapping.Add(item, exprs); while (ops.Next != null) { ops = ops.Next; } Opcode res = ops; while (ops != null) { if (IsExprStarter(ops)) { Opcode start = ops; Opcode p = ops.Prev; ops.DetachFromParent(); ops = ops.Next; while (ops.ID == OpcodeID.Select) { ops = ops.Next; } ops.DetachFromParent(); SubExpr e = null; for (int i = 0; i < this.exprList.Count; ++i) { if (this.exprList[i].FirstOp.Equals(start)) { e = this.exprList[i]; break; } } SubExprOpcode o; if (e == null) { e = new SubExpr(null, start, NewVarID()); this.exprList.Add(e); o = new SubExprOpcode(e); } else { o = e.Add(start, this); } o.Expr.IncRef(); exprs.Add(o.Expr); o.Attach(ops); ops = o; if (p != null) { p.Attach(ops); } } res = ops; ops = ops.Prev; } return res; } internal static bool IsExprStarter(Opcode op) { if (op.ID == OpcodeID.SelectRoot) { return true; } if (op.ID == OpcodeID.XsltInternalFunction) { XPathMessageFunctionCallOpcode fop = (XPathMessageFunctionCallOpcode)op; if (fop.ReturnType == XPathResultType.NodeSet && fop.ArgCount == 0) { return true; } } return false; } internal int NewVarID() { return nextVar++; } internal void Remove(object item) { List exprs; if (this.removalMapping.TryGetValue(item, out exprs)) { for (int i = 0; i < exprs.Count; ++i) { exprs[i].DecRef(this); } this.removalMapping.Remove(item); Renumber(); } } void Renumber() { this.nextVar = 0; for (int i = 0; i < this.exprList.Count; ++i) { this.exprList[i].Renumber(this); } } internal void Trim() { this.exprList.Capacity = this.exprList.Count; for (int i = 0; i < this.exprList.Count; ++i) { this.exprList[i].Trim(); } } #if DEBUG_FILTER internal void Write(TextWriter outStream) { for(int i = 0; i < this.exprList.Count; ++i) { this.exprList[i].Write(outStream); } } #endif } internal class SubExprOpcode : Opcode { protected SubExpr expr; internal SubExprOpcode(SubExpr expr) : base(OpcodeID.SubExpr) { this.expr = expr; } internal SubExpr Expr { get { return expr; } } internal override bool Equals(Opcode op) { if (base.Equals(op)) { SubExprOpcode sop = op as SubExprOpcode; if (sop != null) { return this.expr == sop.expr; } } return false; } internal override Opcode Eval(ProcessingContext context) { if (!context.LoadVariable(this.expr.Variable)) { context.PushSequenceFrame(); NodeSequence seq = context.CreateSequence(); seq.Add(context.Processor.ContextNode); context.PushSequence(seq); int marker = context.Processor.CounterMarker; try { this.expr.Eval(context); } catch (XPathNavigatorException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this)); } catch (NavigatorInvalidBodyAccessException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this)); } context.Processor.CounterMarker = marker; context.PopSequenceFrame(); context.PopSequenceFrame(); context.LoadVariable(this.expr.Variable); } return this.next; } internal override Opcode EvalSpecial(ProcessingContext context) { try { this.expr.EvalSpecial(context); } catch (XPathNavigatorException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this)); } catch (NavigatorInvalidBodyAccessException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this)); } return this.next; } #if DEBUG_FILTER public override string ToString() { return string.Format("{0} #{1}", base.ToString(), this.expr.Variable.ToString()); } #endif } internal class InternalSubExprOpcode : SubExprOpcode { internal InternalSubExprOpcode(SubExpr expr) : base(expr) { } internal override Opcode Eval(ProcessingContext context) { if (!context.LoadVariable(this.expr.Variable)) { this.expr.Eval(context); } return this.next; } internal override Opcode EvalSpecial(ProcessingContext context) { this.expr.EvalSpecial(context); return this.next; } } }