//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Dispatcher { using System.Runtime; using System.Xml.XPath; // The compiler RECURSIVELY consumes xpath expression trees. class XPathCompiler { QueryCompilerFlags flags; int nestingLevel; bool pushInitialContext; #if FILTEROPTIMIZER FilterOptimizer optimizer; internal XPathCompiler(FilterOptimizer optimizer, QueryCompilerFlags flags) { this.optimizer = optimizer; this.flags = flags; this.pushInitialContext = false; } internal XPathCompiler(QueryCompilerFlags flags) : this(new FilterOptimizer(SelectFunctionTree.standard), flags) { } #else internal XPathCompiler(QueryCompilerFlags flags) { this.flags = flags; this.pushInitialContext = false; } #endif void SetPushInitialContext(bool pushInitial) { if (pushInitial) { this.pushInitialContext = pushInitial; } } // 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; } // Implemented as a struct because it is cheap to allocate and the Expression compiler is // allocated a lot! internal struct XPathExprCompiler { OpcodeBlock codeBlock; XPathCompiler compiler; 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 OpcodeBlock Compile(XPathExpr expr) { this.codeBlock = new OpcodeBlock(); // struct this.CompileExpression(expr); return this.codeBlock; } OpcodeBlock CompileBlock(XPathExpr expr) { XPathExprCompiler compiler = new XPathExprCompiler(this); return compiler.Compile(expr); } 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 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); } void CompileExpression(XPathExpr expr) { Fx.Assert(null != expr, ""); switch (expr.Type) { default: this.ThrowError(QueryCompileError.UnsupportedExpression); break; case XPathExprType.And: this.CompileBoolean(expr, true); break; case XPathExprType.Or: this.CompileBoolean(expr, false); break; case XPathExprType.Relational: this.CompileRelational((XPathRelationExpr)expr); break; case XPathExprType.Function: this.CompileFunction((XPathFunctionExpr)expr); break; case XPathExprType.Union: { XPathConjunctExpr unionExpr = (XPathConjunctExpr)expr; this.CompileExpression(unionExpr.Left); this.CompileExpression(unionExpr.Right); this.codeBlock.Append(new UnionOpcode()); } break; case XPathExprType.RelativePath: this.CompileRelativePath(expr, true); break; case XPathExprType.LocationPath: if (expr.SubExprCount > 0) { this.CompileLocationPath(expr); // Step complete. Transfer results onto the value stack this.codeBlock.Append(new PopSequenceToValueStackOpcode()); } break; case XPathExprType.Math: this.CompileMath((XPathMathExpr)expr); break; case XPathExprType.Number: XPathNumberExpr number = (XPathNumberExpr)expr; double literal = number.Number; if (number.Negate) { number.Negate = false; literal = -literal; } this.codeBlock.Append(new PushNumberOpcode(literal)); break; case XPathExprType.String: this.codeBlock.Append(new PushStringOpcode(((XPathStringExpr)expr).String)); break; case XPathExprType.Filter: this.CompileFilter(expr); if (expr.ReturnType == ValueDataType.Sequence) { this.codeBlock.Append(new PopSequenceToValueStackOpcode()); } break; case XPathExprType.Path: this.CompilePath(expr); if (expr.SubExprCount == 0 && expr.ReturnType == ValueDataType.Sequence) { this.codeBlock.Append(new PopSequenceToValueStackOpcode()); } break; case XPathExprType.XsltFunction: this.CompileXsltFunction((XPathXsltFunctionExpr)expr); break; case XPathExprType.XsltVariable: this.CompileXsltVariable((XPathXsltVariableExpr)expr); break; } NegateIfRequired(expr); } void CompileFilter(XPathExpr expr) { Fx.Assert(XPathExprType.Filter == expr.Type, ""); // The filter expression has two components - the expression and its predicate // It may have an optional relative path following it //Debug.Assert(expr.SubExprCount <= 3); XPathExprList subExpr = expr.SubExpr; XPathExpr filterExpr = subExpr[0]; if (subExpr.Count > 1 && ValueDataType.Sequence != filterExpr.ReturnType) { this.ThrowError(QueryCompileError.InvalidExpression); } // The filter expression will return a sequence and push it onto the value stack // Transfer it back to the sequence stack, so we can keep working on it this.CompileExpression(filterExpr); if (filterExpr.ReturnType == ValueDataType.Sequence) { if (!IsSpecialInternalFunction(filterExpr) && expr.SubExprCount > 1) { // Flatten the sequence and move it to the sequence stack this.codeBlock.Append(new MergeOpcode()); this.codeBlock.Append(new PopSequenceToSequenceStackOpcode()); } else if (IsSpecialInternalFunction(filterExpr) && expr.SubExprCount > 1) { this.codeBlock.DetachLast(); } // Now, apply the predicates this.compiler.nestingLevel++; if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ] { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep)); } for (int i = 1; i < expr.SubExprCount; ++i) { this.CompilePredicate(subExpr[i]); } this.compiler.nestingLevel--; } } bool IsSpecialInternalFunction(XPathExpr expr) { if (expr.Type != XPathExprType.XsltFunction) { return false; } XPathMessageFunction func = ((XPathXsltFunctionExpr)expr).Function as XPathMessageFunction; if (func != null) { return func.ReturnType == XPathResultType.NodeSet && func.Maxargs == 0; } return false; } void CompileFunction(XPathFunctionExpr expr) { // In some scenarios, some functions are handled in a special way if (this.CompileFunctionSpecial(expr)) { return; } // Generic function compilation QueryFunction function = expr.Function; // Compile each argument expression first, introducing a typecast where appropriate // Arguments are pushed C style - right to left if (expr.SubExprCount > 0) { XPathExprList paramList = expr.SubExpr; for (int i = paramList.Count - 1; i >= 0; --i) { this.CompileFunctionParam(function, expr.SubExpr, i); } } this.codeBlock.Append(new FunctionCallOpcode(function)); if (1 == this.compiler.nestingLevel && function.TestFlag(QueryFunctionFlag.UsesContextNode)) { this.compiler.SetPushInitialContext(true); } } void CompileFunctionParam(QueryFunction function, XPathExprList paramList, int index) { XPathExpr param = paramList[index]; this.CompileExpression(param); if (ValueDataType.None != function.ParamTypes[index]) { if (param.ReturnType != function.ParamTypes[index]) { if (function.ParamTypes[index] == ValueDataType.Sequence) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.InvalidTypeConversion)); } this.CompileTypecast(function.ParamTypes[index]); } } } // Some functions are compiled with special opcodes to optimize perf in special situations // 1. starts-with(string, literal) bool CompileFunctionSpecial(XPathFunctionExpr expr) { XPathFunction function = expr.Function as XPathFunction; if (null != function) { if (XPathFunctionID.StartsWith == function.ID) { // Does the 2nd parameter start with a string literal? Use a special opcode to handle those.. Fx.Assert(expr.SubExprCount == 2, ""); if (XPathExprType.String == expr.SubExpr[1].Type) { this.CompileFunctionParam(function, expr.SubExpr, 0); this.codeBlock.Append(new StringPrefixOpcode(((XPathStringExpr)expr.SubExpr[1]).String)); return true; } } } return false; } void CompileLiteralRelation(XPathRelationExpr expr) { XPathLiteralExpr left = (XPathLiteralExpr)expr.Left; XPathLiteralExpr right = (XPathLiteralExpr)expr.Right; bool result = QueryValueModel.CompileTimeCompare(left.Literal, right.Literal, expr.Op); this.codeBlock.Append(new PushBooleanOpcode(result)); } void CompileLiteralOrdinal(XPathExpr expr) { int ordinal = 0; try { XPathNumberExpr numExpr = (XPathNumberExpr)expr; ordinal = Convert.ToInt32(numExpr.Number); if (numExpr.Negate) { ordinal = -ordinal; numExpr.Negate = false; } if (ordinal < 1) { this.ThrowError(QueryCompileError.InvalidOrdinal); } } catch (OverflowException) { this.ThrowError(QueryCompileError.InvalidOrdinal); } if (0 != (this.compiler.flags & QueryCompilerFlags.InverseQuery)) { this.codeBlock.Append(new PushContextPositionOpcode()); this.codeBlock.Append(new NumberEqualsOpcode(ordinal)); } else { this.codeBlock.Append(new LiteralOrdinalOpcode(ordinal)); } } void CompileLocationPath(XPathExpr expr) { Fx.Assert(expr.SubExprCount > 0, ""); XPathStepExpr firstStep = (XPathStepExpr)expr.SubExpr[0]; this.CompileSteps(expr.SubExpr); if (1 == this.compiler.nestingLevel) { this.compiler.SetPushInitialContext(firstStep.SelectDesc.Type != QueryNodeType.Root); } } void CompileMath(XPathMathExpr mathExpr) { // are we doing math on two literal numbers? If so, do it at compile time if (XPathExprType.Number == mathExpr.Right.Type && XPathExprType.Number == mathExpr.Left.Type) { double left = ((XPathNumberExpr)mathExpr.Left).Number; if (((XPathNumberExpr)mathExpr.Left).Negate) { ((XPathNumberExpr)mathExpr.Left).Negate = false; left = -left; } double right = ((XPathNumberExpr)mathExpr.Right).Number; if (((XPathNumberExpr)mathExpr.Right).Negate) { ((XPathNumberExpr)mathExpr.Right).Negate = false; right = -right; } switch (mathExpr.Op) { case MathOperator.Div: left /= right; break; case MathOperator.Minus: left -= right; break; case MathOperator.Mod: left %= right; break; case MathOperator.Multiply: left *= right; break; case MathOperator.Plus: left += right; break; } this.codeBlock.Append(new PushNumberOpcode(left)); return; } // Arguments are pushed C style - right to left this.CompileExpression(mathExpr.Right); if (ValueDataType.Double != mathExpr.Right.ReturnType) { this.CompileTypecast(ValueDataType.Double); } this.CompileExpression(mathExpr.Left); if (ValueDataType.Double != mathExpr.Left.ReturnType) { this.CompileTypecast(ValueDataType.Double); } this.codeBlock.Append(this.CreateMathOpcode(mathExpr.Op)); } void CompileNumberLiteralEquality(XPathRelationExpr expr) { Fx.Assert(expr.Op == RelationOperator.Eq, ""); bool leftNumber = (XPathExprType.Number == expr.Left.Type); bool rightNumber = (XPathExprType.Number == expr.Right.Type); Fx.Assert(leftNumber || rightNumber, ""); Fx.Assert(!(leftNumber && rightNumber), ""); this.CompileExpression(leftNumber ? expr.Right : expr.Left); XPathNumberExpr litExpr = leftNumber ? (XPathNumberExpr)expr.Left : (XPathNumberExpr)expr.Right; double literal = litExpr.Number; if (litExpr.Negate) { litExpr.Negate = false; literal = -literal; } this.codeBlock.Append(new NumberEqualsOpcode(literal)); } void CompileNumberRelation(XPathRelationExpr expr) { if (expr.Op == RelationOperator.Eq) { this.CompileNumberLiteralEquality(expr); return; } bool leftNumber = (XPathExprType.Number == expr.Left.Type); bool rightNumber = (XPathExprType.Number == expr.Right.Type); Fx.Assert(leftNumber || rightNumber, ""); Fx.Assert(!(leftNumber && rightNumber), ""); this.CompileExpression(leftNumber ? expr.Right : expr.Left); XPathNumberExpr litExpr = leftNumber ? (XPathNumberExpr)expr.Left : (XPathNumberExpr)expr.Right; double literal = litExpr.Number; if (litExpr.Negate) { litExpr.Negate = false; literal = -literal; } // To maximize code branch commonality, we canonacalize the relation expressions so that the non-literal // is always to the left and the literal to the right. If this makes us swap expressions, we must also flip // relation operators appropriately. if (leftNumber) { // Flip operators switch (expr.Op) { case RelationOperator.Gt: expr.Op = RelationOperator.Lt; break; case RelationOperator.Ge: expr.Op = RelationOperator.Le; break; case RelationOperator.Lt: expr.Op = RelationOperator.Gt; break; case RelationOperator.Le: expr.Op = RelationOperator.Ge; break; } } if (0 != (this.compiler.flags & QueryCompilerFlags.InverseQuery)) { this.codeBlock.Append(new NumberIntervalOpcode(literal, expr.Op)); } else { this.codeBlock.Append(new NumberRelationOpcode(literal, expr.Op)); } } void CompilePath(XPathExpr expr) { Fx.Assert(expr.SubExprCount == 2 || expr.SubExprCount == 3, ""); if (expr.Type == XPathExprType.Filter) { this.CompileFilter(expr.SubExpr[0]); } else { this.CompileExpression(expr.SubExpr[0]); if (expr.SubExpr[0].ReturnType == ValueDataType.Sequence) { if (IsSpecialInternalFunction(expr.SubExpr[0])) { this.codeBlock.DetachLast(); } else { this.codeBlock.Append(new MergeOpcode()); this.codeBlock.Append(new PopSequenceToSequenceStackOpcode()); } } } if (expr.SubExprCount == 2) { this.CompileRelativePath(expr.SubExpr[1], false); } else if (expr.SubExprCount == 3) { // Compile the step XPathExpr e = expr.SubExpr[1]; Fx.Assert(XPathExprType.PathStep == e.Type, ""); XPathStepExpr step = (XPathStepExpr)e; Fx.Assert(QueryNodeType.Root != step.SelectDesc.Type, ""); if (!step.SelectDesc.Axis.IsSupported()) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.UnsupportedAxis)); } this.codeBlock.Append(new SelectOpcode(step.SelectDesc)); // The step may have predicates.. if (step.SubExprCount > 0) { this.compiler.nestingLevel++; if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ] { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep)); } this.CompilePredicates(step.SubExpr); this.compiler.nestingLevel--; } // Compile the relative path this.CompileRelativePath(expr.SubExpr[2], false); } } void CompilePredicate(XPathExpr expr) { // If the expression does not return a boolean, introduce a typecast // If the predicate expression is a standalone number literal, interpret it as a literal if (expr.IsLiteral && XPathExprType.Number == expr.Type) { this.CompileLiteralOrdinal(expr); } else { this.CompileExpression(expr); if (expr.ReturnType == ValueDataType.Double) { this.codeBlock.Append(new OrdinalOpcode()); } else if (expr.ReturnType != ValueDataType.Boolean) { this.CompileTypecast(ValueDataType.Boolean); } } // Apply the results of the predicate on the context sequence this.codeBlock.Append(new ApplyFilterOpcode()); } void CompilePredicates(XPathExprList exprList) { // Compile each predicate expression first for (int i = 0; i < exprList.Count; ++i) { this.CompilePredicate(exprList[i]); } } void CompileRelational(XPathRelationExpr expr) { // Are we comparing two literals? if (expr.Left.IsLiteral && expr.Right.IsLiteral) { // Do the comparison at compile time this.CompileLiteralRelation(expr); return; } // != is not optimized in M5 if (expr.Op != RelationOperator.Ne) { // Number relations are handled in a special way if (XPathExprType.Number == expr.Left.Type || XPathExprType.Number == expr.Right.Type) { this.CompileNumberRelation(expr); return; } // Equality tests with string literals are handled in a special way if (expr.Op == RelationOperator.Eq && (XPathExprType.String == expr.Left.Type || XPathExprType.String == expr.Right.Type)) { this.CompileStringLiteralEquality(expr); return; } } // Can't optimize. Use a general purpose relation opcode this.CompileExpression(expr.Left); this.CompileExpression(expr.Right); this.codeBlock.Append(new RelationOpcode(expr.Op)); } void CompileRelativePath(XPathExpr expr, bool start) { Fx.Assert(XPathExprType.RelativePath == expr.Type, ""); this.CompileSteps(expr.SubExpr, start); // Step complete. Transfer results onto the value stack this.codeBlock.Append(new PopSequenceToValueStackOpcode()); } void CompileStringLiteralEquality(XPathRelationExpr expr) { Fx.Assert(expr.Op == RelationOperator.Eq, ""); bool leftString = (XPathExprType.String == expr.Left.Type); bool rightString = (XPathExprType.String == expr.Right.Type); Fx.Assert(leftString || rightString, ""); Fx.Assert(!(leftString && rightString), ""); this.CompileExpression(leftString ? expr.Right : expr.Left); string literal = leftString ? ((XPathStringExpr)expr.Left).String : ((XPathStringExpr)expr.Right).String; this.codeBlock.Append(new StringEqualsOpcode(literal)); } void CompileSteps(XPathExprList steps) { CompileSteps(steps, true); } void CompileSteps(XPathExprList steps, bool start) { for (int i = 0; i < steps.Count; ++i) { Fx.Assert(XPathExprType.PathStep == steps[i].Type, ""); XPathStepExpr step = (XPathStepExpr)steps[i]; if (!step.SelectDesc.Axis.IsSupported()) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.UnsupportedAxis)); } Opcode stepOpcode = null; if (start && 0 == i) { // First steps // Is this an absolute path? We have an absolute path if the first step selects the root if (QueryNodeType.Root == step.SelectDesc.Type) { stepOpcode = new SelectRootOpcode(); } else { stepOpcode = new InitialSelectOpcode(step.SelectDesc); } } else { Fx.Assert(QueryNodeType.Root != step.SelectDesc.Type, ""); stepOpcode = new SelectOpcode(step.SelectDesc); } this.codeBlock.Append(stepOpcode); // The step may have predicates.. if (step.SubExprCount > 0) { this.compiler.nestingLevel++; if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ] { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep)); } this.CompilePredicates(step.SubExpr); this.compiler.nestingLevel--; } } } void CompileTypecast(ValueDataType destType) { Fx.Assert(ValueDataType.None != destType, ""); this.codeBlock.Append(new TypecastOpcode(destType)); } void CompileXsltFunction(XPathXsltFunctionExpr expr) { // Compile each argument expression first, introducing a typecast where appropriate // Arguments are pushed C style - right to left if (expr.SubExprCount > 0) { XPathExprList paramList = expr.SubExpr; for (int i = paramList.Count - 1; i >= 0; --i) { XPathExpr param = paramList[i]; this.CompileExpression(param); ValueDataType paramType = XPathXsltFunctionExpr.ConvertTypeFromXslt(expr.Function.ArgTypes[i]); if (ValueDataType.None != paramType) { if (param.ReturnType != paramType) { this.CompileTypecast(paramType); } } } } if (expr.Function is XPathMessageFunction) { this.codeBlock.Append(new XPathMessageFunctionCallOpcode((XPathMessageFunction)expr.Function, expr.SubExprCount)); if (IsSpecialInternalFunction(expr)) { this.codeBlock.Append(new PopSequenceToValueStackOpcode()); } } else { this.codeBlock.Append(new XsltFunctionCallOpcode(expr.Context, expr.Function, expr.SubExprCount)); } } void CompileXsltVariable(XPathXsltVariableExpr expr) { #if NO // Remove this block if we never decide to use variables in an XPathMessageContext // It is here in case we decide to if (expr.Variable is XPathMessageVariable) { this.codeBlock.Append(new PushXPathMessageVariableOpcode((XPathMessageVariable)expr.Variable)); } else { this.codeBlock.Append(new PushXsltVariableOpcode(expr.Context, expr.Variable)); } #endif this.codeBlock.Append(new PushXsltVariableOpcode(expr.Context, expr.Variable)); } MathOpcode CreateMathOpcode(MathOperator op) { MathOpcode opcode = null; switch (op) { case MathOperator.None: Fx.Assert(""); break; case MathOperator.Plus: opcode = new PlusOpcode(); break; case MathOperator.Minus: opcode = new MinusOpcode(); break; case MathOperator.Div: opcode = new DivideOpcode(); break; case MathOperator.Multiply: opcode = new MultiplyOpcode(); break; case MathOperator.Mod: opcode = new ModulusOpcode(); break; case MathOperator.Negate: opcode = new NegateOpcode(); break; } return opcode; } void NegateIfRequired(XPathExpr expr) { // We can combine these two since the flags they examine are set in exactly one (the same) place. TypecastIfRequired(expr); if (expr.Negate) { expr.Negate = false; this.codeBlock.Append(new NegateOpcode()); } } void TypecastIfRequired(XPathExpr expr) { if (expr.TypecastRequired) { expr.TypecastRequired = false; CompileTypecast(expr.ReturnType); } } void ThrowError(QueryCompileError error) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(error)); } } } }