private void CompileBoolean(XPathExpr expr, bool testValue) { if (this.compiler.nestingLevel == 1) { this.CompileBasicBoolean(expr, testValue); } else { OpcodeBlock block = new OpcodeBlock(); Opcode jump = new BlockEndOpcode(); block.Append(new PushBooleanOpcode(testValue)); XPathExprList subExpr = expr.SubExpr; XPathExpr expr2 = subExpr[0]; block.Append(this.CompileBlock(expr2)); if (expr2.ReturnType != ValueDataType.Boolean) { block.Append(new TypecastOpcode(ValueDataType.Boolean)); } block.Append(new ApplyBooleanOpcode(jump, testValue)); for (int i = 1; i < subExpr.Count; i++) { expr2 = subExpr[i]; block.Append(new StartBooleanOpcode(testValue)); block.Append(this.CompileBlock(expr2)); if (expr2.ReturnType != ValueDataType.Boolean) { block.Append(new TypecastOpcode(ValueDataType.Boolean)); } block.Append(new EndBooleanOpcode(jump, testValue)); } block.Append(jump); this.codeBlock.Append(block); } }
internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager nsManager, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType) { OpcodeBlock codeBlock; returnType = ValueDataType.None; if (0 == xpath.Length) { // 0 length XPaths always match codeBlock = new OpcodeBlock(); codeBlock.Append(new PushBooleanOpcode(true)); // Always match by pushing true on the eval stack } else { // Try to parse the xpath. Bind to default function libraries // The parser returns an expression tree XPathParser parser = new XPathParser(xpath, nsManager, functionLibs); XPathExpr parseTree = parser.Parse(); if (null == parseTree) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.CouldNotParseExpression)); } returnType = parseTree.ReturnType; // Compile the expression tree XPathCompiler compiler = new XPathCompiler(flags); codeBlock = compiler.Compile(parseTree); } return(codeBlock); }
// Compiles expressions at nesting level == 1 -> boolean expressions that can be processed // with less complex opcodes because they will never track multiple sequences simultaneously void CompileBasicBoolean(XPathExpr expr, bool testValue) { // Boolean expressions must always have at least 2 sub expressions Fx.Assert(expr.SubExprCount > 1, ""); Fx.Assert(this.compiler.nestingLevel == 1, ""); OpcodeBlock boolBlock = new OpcodeBlock(); // struct Opcode blockEnd = new BlockEndOpcode(); XPathExprList subExprList = expr.SubExpr; // Compile sub-expressions for (int i = 0; i < subExprList.Count; ++i) { XPathExpr subExpr = subExprList[i]; boolBlock.Append(this.CompileBlock(subExpr)); // Make sure each sub-expression can produce a boolean result if (subExpr.ReturnType != ValueDataType.Boolean) { boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean)); } if (i < (subExprList.Count - 1)) { // No point jumping if this is the last expression boolBlock.Append(new JumpIfOpcode(blockEnd, testValue)); } } boolBlock.Append(blockEnd); this.codeBlock.Append(boolBlock); }
static XPathQueryMatcher() { ValueDataType type; matchAlwaysFilter = new PushBooleanOpcode(true); rootFilter = QueryMatcher.CompileForInternalEngine("/", null, QueryCompilerFlags.None, out type); rootFilter.Append(new MatchResultOpcode()); }
internal void CompileForInternal(string xpath, XmlNamespaceManager names) { base.query = null; xpath = xpath.Trim(); if (xpath.Length == 0) { base.query = matchAlwaysFilter; this.flags |= XPathFilterFlags.AlwaysMatch; } else if ((1 == xpath.Length) && ('/' == xpath[0])) { base.query = rootFilter.First; this.flags |= XPathFilterFlags.AlwaysMatch; } else { ValueDataType type; OpcodeBlock block = QueryMatcher.CompileForInternalEngine(xpath, names, QueryCompilerFlags.None, out type); if (this.match) { block.Append(new MatchResultOpcode()); } else { block.Append(new QueryResultOpcode()); } base.query = block.First; } this.flags &= ~XPathFilterFlags.IsFxFilter; }
internal void Add(string expression, XmlNamespaceManager names, object item, bool forceExternal) { Fx.Assert(null != item, ""); // Compile the new filter bool compiled = false; OpcodeBlock codeBlock = new OpcodeBlock(); codeBlock.Append(new NoOpOpcode(OpcodeID.QueryTree)); if (!forceExternal) { try { ValueDataType returnType = ValueDataType.None; // Try to compile and merge the compiled query into the query tree codeBlock.Append(QueryMatcher.CompileForInternalEngine(expression, names, QueryCompilerFlags.InverseQuery, out returnType)); MultipleResultOpcode opcode; if (!this.match) { opcode = new QueryMultipleResultOpcode(); } else { opcode = new MatchMultipleResultOpcode(); } opcode.AddItem(item); codeBlock.Append(opcode); compiled = true; // Perform SubExpression Elimination codeBlock = new OpcodeBlock(this.elim.Add(item, codeBlock.First)); this.subExprVars = this.elim.VariableCount; } catch (QueryCompileException) { // If the filter couldn't be compiled, we drop down to the framework engine } } if (!compiled) { codeBlock.Append(QueryMatcher.CompileForExternalEngine(expression, names, item, this.match)); } // Merge the compiled query into the query tree QueryTreeBuilder builder = new QueryTreeBuilder(); this.query = builder.Build(this.query, codeBlock); // To de-merge this filter from the tree, we'll have to walk backwards up the tree... so we // have to remember the last opcode that is executed on behalf of this filter this.lastLookup[item] = builder.LastOpcode; }
/// <summary> /// Compile the given filter to run on an external (fx) xpath engine /// </summary> internal static OpcodeBlock CompileForExternalEngine(string expression, XmlNamespaceManager namespaces, object item, bool match) { // Compile... XPathExpression xpathExpr = QueryMatcher.fxCompiler.Compile(expression); // Fx will bind prefixes and functions here. if (namespaces != null) { // There's a bug in System.Xml.XPath. If we pass an XsltContext to SetContext it won't throw if there's // an undefined prefix. if (namespaces is XsltContext) { // Lex the xpath to find all prefixes used XPathLexer lexer = new XPathLexer(expression, false); while (lexer.MoveNext()) { string prefix = lexer.Token.Prefix; if (prefix.Length > 0 && namespaces.LookupNamespace(prefix) == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(SR.GetString(SR.FilterUndefinedPrefix, prefix))); } } } xpathExpr.SetContext(namespaces); } // // FORCE the function to COMPILE - they won't bind namespaces unless we check the return type // if (XPathResultType.Error == xpathExpr.ReturnType) { // This should never be reached. The above property should throw if there's an error throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.FilterCouldNotCompile, expression))); } OpcodeBlock codeBlock = new OpcodeBlock(); SingleFxEngineResultOpcode op; if (!match) { op = new QuerySingleFxEngineResultOpcode(); } else { op = new MatchSingleFxEngineResultOpcode(); } op.XPath = xpathExpr; op.Item = item; codeBlock.Append(op); return(codeBlock); }
internal void Append(OpcodeBlock block) { if (this.last == null) { this.first = block.first; this.last = block.last; } else { this.last.Attach(block.first); this.last = block.last; } }
internal virtual OpcodeBlock Compile(XPathExpr expr) { this.nestingLevel = 1; this.pushInitialContext = false; OpcodeBlock block = new XPathExprCompiler(this).Compile(expr); if (this.pushInitialContext) { OpcodeBlock block2 = new OpcodeBlock(); block2.Append(new PushContextNodeOpcode()); block2.Append(block); block2.Append(new PopContextNodes()); return block2; } return block; }
internal virtual OpcodeBlock Compile(XPathExpr expr) { this.nestingLevel = 1; this.pushInitialContext = false; OpcodeBlock block = new XPathExprCompiler(this).Compile(expr); if (this.pushInitialContext) { OpcodeBlock block2 = new OpcodeBlock(); block2.Append(new PushContextNodeOpcode()); block2.Append(block); block2.Append(new PopContextNodes()); return(block2); } return(block); }
internal Opcode Build(Opcode tree, OpcodeBlock newBlock) { if (tree == null) { this.lastOpcode = newBlock.Last; return newBlock.First; } this.diverger = new Diverger(tree, newBlock.First); if (!this.diverger.Find()) { this.lastOpcode = this.diverger.TreePath[this.diverger.TreePath.Count - 1]; return tree; } if (this.diverger.TreeOpcode == null) { this.diverger.TreePath[this.diverger.TreePath.Count - 1].Attach(this.diverger.InsertOpcode); } else { this.diverger.TreeOpcode.Add(this.diverger.InsertOpcode); } this.lastOpcode = newBlock.Last; if (this.diverger.InsertOpcode.IsMultipleResult()) { if (OpcodeID.Branch == this.diverger.TreeOpcode.ID) { OpcodeList branches = ((BranchOpcode) this.diverger.TreeOpcode).Branches; int num = 0; int count = branches.Count; while (num < count) { if (branches[num].IsMultipleResult()) { this.lastOpcode = branches[num]; break; } num++; } } else if (this.diverger.TreeOpcode.IsMultipleResult()) { this.lastOpcode = this.diverger.TreeOpcode; } } this.FixupJumps(); return tree; }
internal Opcode Build(Opcode tree, OpcodeBlock newBlock) { if (tree == null) { this.lastOpcode = newBlock.Last; return(newBlock.First); } this.diverger = new Diverger(tree, newBlock.First); if (!this.diverger.Find()) { this.lastOpcode = this.diverger.TreePath[this.diverger.TreePath.Count - 1]; return(tree); } if (this.diverger.TreeOpcode == null) { this.diverger.TreePath[this.diverger.TreePath.Count - 1].Attach(this.diverger.InsertOpcode); } else { this.diverger.TreeOpcode.Add(this.diverger.InsertOpcode); } this.lastOpcode = newBlock.Last; if (this.diverger.InsertOpcode.IsMultipleResult()) { if (OpcodeID.Branch == this.diverger.TreeOpcode.ID) { OpcodeList branches = ((BranchOpcode)this.diverger.TreeOpcode).Branches; int num = 0; int count = branches.Count; while (num < count) { if (branches[num].IsMultipleResult()) { this.lastOpcode = branches[num]; break; } num++; } } else if (this.diverger.TreeOpcode.IsMultipleResult()) { this.lastOpcode = this.diverger.TreeOpcode; } } this.FixupJumps(); return(tree); }
void CompileBoolean(XPathExpr expr, bool testValue) { // Boolean expressions must always have at least 2 sub expressions Fx.Assert(expr.SubExprCount > 1, ""); if (this.compiler.nestingLevel == 1) { this.CompileBasicBoolean(expr, testValue); return; } OpcodeBlock boolBlock = new OpcodeBlock(); // struct Opcode blockEnd = new BlockEndOpcode(); // Set up the result mask boolBlock.Append(new PushBooleanOpcode(testValue)); XPathExprList subExprList = expr.SubExpr; XPathExpr subExpr; // the first expression needs the least work.. subExpr = subExprList[0]; boolBlock.Append(this.CompileBlock(subExpr)); if (subExpr.ReturnType != ValueDataType.Boolean) { boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean)); } boolBlock.Append(new ApplyBooleanOpcode(blockEnd, testValue)); // Compile remaining sub-expressions for (int i = 1; i < subExprList.Count; ++i) { subExpr = subExprList[i]; boolBlock.Append(new StartBooleanOpcode(testValue)); boolBlock.Append(this.CompileBlock(subExpr)); // Make sure each sub-expression can produce a boolean result if (subExpr.ReturnType != ValueDataType.Boolean) { boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean)); } boolBlock.Append(new EndBooleanOpcode(blockEnd, testValue)); } boolBlock.Append(blockEnd); this.codeBlock.Append(boolBlock); }
// Compiles top level expressions internal virtual OpcodeBlock Compile(XPathExpr expr) { Fx.Assert(null != expr, ""); this.nestingLevel = 1; this.pushInitialContext = false; XPathExprCompiler exprCompiler = new XPathExprCompiler(this); OpcodeBlock mainBlock = exprCompiler.Compile(expr); if (this.pushInitialContext) { OpcodeBlock expandedBlock = new OpcodeBlock(); expandedBlock.Append(new PushContextNodeOpcode()); expandedBlock.Append(mainBlock); expandedBlock.Append(new PopContextNodes()); return expandedBlock; } return mainBlock; }
// Compiles top level expressions internal virtual OpcodeBlock Compile(XPathExpr expr) { Fx.Assert(null != expr, ""); this.nestingLevel = 1; this.pushInitialContext = false; XPathExprCompiler exprCompiler = new XPathExprCompiler(this); OpcodeBlock mainBlock = exprCompiler.Compile(expr); if (this.pushInitialContext) { OpcodeBlock expandedBlock = new OpcodeBlock(); expandedBlock.Append(new PushContextNodeOpcode()); expandedBlock.Append(mainBlock); expandedBlock.Append(new PopContextNodes()); return(expandedBlock); } return(mainBlock); }
internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager nsManager, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType) { returnType = ValueDataType.None; if (xpath.Length == 0) { OpcodeBlock block = new OpcodeBlock(); block.Append(new PushBooleanOpcode(true)); return(block); } XPathExpr expr = new XPathParser(xpath, nsManager, functionLibs).Parse(); if (expr == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.CouldNotParseExpression)); } returnType = expr.ReturnType; XPathCompiler compiler = new XPathCompiler(flags); return(compiler.Compile(expr)); }
internal static OpcodeBlock CompileForExternalEngine(string expression, XmlNamespaceManager namespaces, object item, bool match) { SingleFxEngineResultOpcode opcode; XPathExpression expression2 = fxCompiler.Compile(expression); if (namespaces != null) { if (namespaces is XsltContext) { XPathLexer lexer = new XPathLexer(expression, false); while (lexer.MoveNext()) { string prefix = lexer.Token.Prefix; if ((prefix.Length > 0) && (namespaces.LookupNamespace(prefix) == null)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(System.ServiceModel.SR.GetString("FilterUndefinedPrefix", new object[] { prefix }))); } } } expression2.SetContext(namespaces); } if (XPathResultType.Error == expression2.ReturnType) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(System.ServiceModel.SR.GetString("FilterCouldNotCompile", new object[] { expression }))); } OpcodeBlock block = new OpcodeBlock(); if (!match) { opcode = new QuerySingleFxEngineResultOpcode(); } else { opcode = new MatchSingleFxEngineResultOpcode(); } opcode.XPath = expression2; opcode.Item = item; block.Append(opcode); return(block); }
internal void Add(string expression, XmlNamespaceManager names, object item, bool forceExternal) { bool flag = false; OpcodeBlock newBlock = new OpcodeBlock(); newBlock.Append(new NoOpOpcode(OpcodeID.QueryTree)); if (!forceExternal) { try { MultipleResultOpcode opcode; ValueDataType none = ValueDataType.None; newBlock.Append(QueryMatcher.CompileForInternalEngine(expression, names, QueryCompilerFlags.InverseQuery, out none)); if (!this.match) { opcode = new QueryMultipleResultOpcode(); } else { opcode = new MatchMultipleResultOpcode(); } opcode.AddItem(item); newBlock.Append(opcode); flag = true; newBlock = new OpcodeBlock(this.elim.Add(item, newBlock.First)); base.subExprVars = this.elim.VariableCount; } catch (QueryCompileException) { } } if (!flag) { newBlock.Append(QueryMatcher.CompileForExternalEngine(expression, names, item, this.match)); } QueryTreeBuilder builder = new QueryTreeBuilder(); base.query = builder.Build(base.query, newBlock); this.lastLookup[item] = builder.LastOpcode; }
private void CompileBasicBoolean(XPathExpr expr, bool testValue) { OpcodeBlock block = new OpcodeBlock(); Opcode jump = new BlockEndOpcode(); XPathExprList subExpr = expr.SubExpr; for (int i = 0; i < subExpr.Count; i++) { XPathExpr expr2 = subExpr[i]; block.Append(this.CompileBlock(expr2)); if (expr2.ReturnType != ValueDataType.Boolean) { block.Append(new TypecastOpcode(ValueDataType.Boolean)); } if (i < (subExpr.Count - 1)) { block.Append(new JumpIfOpcode(jump, testValue)); } } block.Append(jump); this.codeBlock.Append(block); }
/// <summary> /// Compile for the internal engine with default flags /// By defalt, we compile an xpath to run stand alone, with standard optimizations /// </summary> internal void CompileForInternal(string xpath, XmlNamespaceManager names) { this.query = null; xpath = xpath.Trim(); if (0 == xpath.Length) { // Empty xpaths always match. Same for xpaths that refer to the root only // We will evaluate such filters with minimal overhead. However, we // don't want a null value for this.query, so we stick a dummy value in there this.query = XPathQueryMatcher.matchAlwaysFilter; this.flags |= (XPathFilterFlags.AlwaysMatch); } else if (1 == xpath.Length && '/' == xpath[0]) { this.query = XPathQueryMatcher.rootFilter.First; this.flags |= (XPathFilterFlags.AlwaysMatch); } else { ValueDataType returnType; OpcodeBlock codeBlock = QueryMatcher.CompileForInternalEngine(xpath, names, QueryCompilerFlags.None, out returnType); // Inject a final opcode that will place the query result on the query context // This query is now ready for execution STAND ALONE if (this.match) { codeBlock.Append(new MatchResultOpcode()); } else { codeBlock.Append(new QueryResultOpcode()); } this.query = codeBlock.First; } this.flags &= ~XPathFilterFlags.IsFxFilter; }
internal Opcode Build(Opcode tree, OpcodeBlock newBlock) { if (null == tree) { this.lastOpcode = newBlock.Last; return newBlock.First; } this.diverger = new Diverger(tree, newBlock.First); if (!this.diverger.Find()) { // The opcodes in newBlock already have equivalents or identical opcodes // in the query tree that can do the job Fx.Assert(this.diverger.TreePath.Count > 0, ""); this.lastOpcode = this.diverger.TreePath[this.diverger.TreePath.Count - 1]; return tree; } Fx.Assert(this.diverger.TreePath.Count == this.diverger.InsertPath.Count, ""); // We can reuse opcodes upto this.diverger.TreePath[this.diverger.TreePath.Count - 1] // The remainder of the code in newBlock must be executed as is... if (null == this.diverger.TreeOpcode) { // We reached a leaf in the query tree // Simply add the remainder of the inserted code to the end of the tree path.. this.diverger.TreePath[this.diverger.TreePath.Count - 1].Attach(this.diverger.InsertOpcode); } else { // Merge in the remaider of the new code block into the query tree // The first diverging opcodes follow the last entry in each path this.diverger.TreeOpcode.Add(this.diverger.InsertOpcode); } this.lastOpcode = newBlock.Last; if (this.diverger.InsertOpcode.IsMultipleResult()) { // The complete new block was merged in, except for the the actual result opcode, which never // automatically merges. This means that the new block found all of its opcodes in common with // the tree if (OpcodeID.Branch == this.diverger.TreeOpcode.ID) { OpcodeList branches = (((BranchOpcode) this.diverger.TreeOpcode).Branches); for (int i = 0, count = branches.Count; i < count; ++i) { if (branches[i].IsMultipleResult()) { this.lastOpcode = branches[i]; break; } } } else if (this.diverger.TreeOpcode.IsMultipleResult()) { this.lastOpcode = this.diverger.TreeOpcode; } } // Since we'll be diverging, any jumps that preceeded and leapt past the divergence point // will have to be branched this.FixupJumps(); return tree; }
internal OpcodeBlock Compile(XPathExpr expr) { this.codeBlock = new OpcodeBlock(); // struct this.CompileExpression(expr); return this.codeBlock; }
internal XPathExprCompiler(XPathCompiler compiler) { Fx.Assert(null != compiler, ""); this.compiler = compiler; this.codeBlock = new OpcodeBlock(); }
XPathExprCompiler(XPathExprCompiler xpathCompiler) { this.compiler = xpathCompiler.compiler; this.codeBlock = new OpcodeBlock(); }
internal Opcode Build(Opcode tree, OpcodeBlock newBlock) { if (null == tree) { this.lastOpcode = newBlock.Last; return(newBlock.First); } this.diverger = new Diverger(tree, newBlock.First); if (!this.diverger.Find()) { // The opcodes in newBlock already have equivalents or identical opcodes // in the query tree that can do the job Fx.Assert(this.diverger.TreePath.Count > 0, ""); this.lastOpcode = this.diverger.TreePath[this.diverger.TreePath.Count - 1]; return(tree); } Fx.Assert(this.diverger.TreePath.Count == this.diverger.InsertPath.Count, ""); // We can reuse opcodes upto this.diverger.TreePath[this.diverger.TreePath.Count - 1] // The remainder of the code in newBlock must be executed as is... if (null == this.diverger.TreeOpcode) { // We reached a leaf in the query tree // Simply add the remainder of the inserted code to the end of the tree path.. this.diverger.TreePath[this.diverger.TreePath.Count - 1].Attach(this.diverger.InsertOpcode); } else { // Merge in the remaider of the new code block into the query tree // The first diverging opcodes follow the last entry in each path this.diverger.TreeOpcode.Add(this.diverger.InsertOpcode); } this.lastOpcode = newBlock.Last; if (this.diverger.InsertOpcode.IsMultipleResult()) { // The complete new block was merged in, except for the the actual result opcode, which never // automatically merges. This means that the new block found all of its opcodes in common with // the tree if (OpcodeID.Branch == this.diverger.TreeOpcode.ID) { OpcodeList branches = (((BranchOpcode)this.diverger.TreeOpcode).Branches); for (int i = 0, count = branches.Count; i < count; ++i) { if (branches[i].IsMultipleResult()) { this.lastOpcode = branches[i]; break; } } } else if (this.diverger.TreeOpcode.IsMultipleResult()) { this.lastOpcode = this.diverger.TreeOpcode; } } // Since we'll be diverging, any jumps that preceeded and leapt past the divergence point // will have to be branched this.FixupJumps(); return(tree); }
internal XPathExprCompiler(XPathCompiler compiler) { this.compiler = compiler; this.codeBlock = new OpcodeBlock(); }
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; } }
private XPathExprCompiler(XPathCompiler.XPathExprCompiler xpathCompiler) { this.compiler = xpathCompiler.compiler; this.codeBlock = new OpcodeBlock(); }
internal OpcodeBlock Compile(XPathExpr expr) { this.codeBlock = new OpcodeBlock(); this.CompileExpression(expr); return(this.codeBlock); }