File: System\ServiceModel\Dispatcher\QuerySubExprEliminator.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// 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<SubExpr> children;
 
        internal SubExpr(SubExpr parent, Opcode ops, int var)
        {
            this.children = new List<SubExpr>(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<string, Dictionary<string, List<SubExpr>>> nameLookup;
        Dictionary<SubExpr, MyInt> indexLookup;
 
        internal SubExprHeader(Opcode ops, int var)
            : base(null, ops, var)
        {
            this.nameLookup = new Dictionary<string, Dictionary<string, List<SubExpr>>>();
            this.indexLookup = new Dictionary<SubExpr, MyInt>();
            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<string, List<SubExpr>> nextLookup;
                if (!this.nameLookup.TryGetValue(ns, out nextLookup))
                {
                    nextLookup = new Dictionary<string, List<SubExpr>>();
                    this.nameLookup.Add(ns, nextLookup);
                }
 
                string name = qname.Name;
                List<SubExpr> exprs = new List<SubExpr>();
                if (!nextLookup.TryGetValue(name, out exprs))
                {
                    exprs = new List<SubExpr>();
                    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<SubExpr> lst;
                        string name = nav.LocalName;
                        string ns = nav.NamespaceURI;
                        Dictionary<string, List<SubExpr>> 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<string, List<SubExpr>> nextLookup;
                if (this.nameLookup.TryGetValue(ns, out nextLookup))
                {
                    string name = qname.Name;
                    List<SubExpr> 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<SubExpr> exprList;
        int nextVar;
        Dictionary<object, List<SubExpr>> removalMapping;
 
        internal SubExprEliminator()
        {
            this.removalMapping = new Dictionary<object, List<SubExpr>>();
            this.exprList = new List<SubExpr>();
 
            Opcode op = new XPathMessageFunctionCallOpcode(XPathMessageContext.HeaderFun, 0);
            SubExprHeader header = new SubExprHeader(op, 0);
            this.exprList.Add(header);
            this.nextVar = 1;
        }
 
        internal List<SubExpr> Exprs
        {
            get
            {
                return this.exprList;
            }
        }
 
        internal int VariableCount
        {
            get
            {
                return this.nextVar;
            }
        }
 
        internal Opcode Add(object item, Opcode ops)
        {
            List<SubExpr> exprs = new List<SubExpr>();
            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<SubExpr> 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;
        }
    }
}