536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
742 lines
22 KiB
C#
742 lines
22 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
}
|