| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901 |
- //------------------------------------------------------------
- // 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));
- }
- }
- }
- }
|