2
0

XPathCompiler.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System.Runtime;
  7. using System.Xml.XPath;
  8. // The compiler RECURSIVELY consumes xpath expression trees.
  9. class XPathCompiler
  10. {
  11. QueryCompilerFlags flags;
  12. int nestingLevel;
  13. bool pushInitialContext;
  14. #if FILTEROPTIMIZER
  15. FilterOptimizer optimizer;
  16. internal XPathCompiler(FilterOptimizer optimizer, QueryCompilerFlags flags)
  17. {
  18. this.optimizer = optimizer;
  19. this.flags = flags;
  20. this.pushInitialContext = false;
  21. }
  22. internal XPathCompiler(QueryCompilerFlags flags)
  23. : this(new FilterOptimizer(SelectFunctionTree.standard), flags)
  24. {
  25. }
  26. #else
  27. internal XPathCompiler(QueryCompilerFlags flags)
  28. {
  29. this.flags = flags;
  30. this.pushInitialContext = false;
  31. }
  32. #endif
  33. void SetPushInitialContext(bool pushInitial)
  34. {
  35. if (pushInitial)
  36. {
  37. this.pushInitialContext = pushInitial;
  38. }
  39. }
  40. // Compiles top level expressions
  41. internal virtual OpcodeBlock Compile(XPathExpr expr)
  42. {
  43. Fx.Assert(null != expr, "");
  44. this.nestingLevel = 1;
  45. this.pushInitialContext = false;
  46. XPathExprCompiler exprCompiler = new XPathExprCompiler(this);
  47. OpcodeBlock mainBlock = exprCompiler.Compile(expr);
  48. if (this.pushInitialContext)
  49. {
  50. OpcodeBlock expandedBlock = new OpcodeBlock();
  51. expandedBlock.Append(new PushContextNodeOpcode());
  52. expandedBlock.Append(mainBlock);
  53. expandedBlock.Append(new PopContextNodes());
  54. return expandedBlock;
  55. }
  56. return mainBlock;
  57. }
  58. // Implemented as a struct because it is cheap to allocate and the Expression compiler is
  59. // allocated a lot!
  60. internal struct XPathExprCompiler
  61. {
  62. OpcodeBlock codeBlock;
  63. XPathCompiler compiler;
  64. internal XPathExprCompiler(XPathCompiler compiler)
  65. {
  66. Fx.Assert(null != compiler, "");
  67. this.compiler = compiler;
  68. this.codeBlock = new OpcodeBlock();
  69. }
  70. XPathExprCompiler(XPathExprCompiler xpathCompiler)
  71. {
  72. this.compiler = xpathCompiler.compiler;
  73. this.codeBlock = new OpcodeBlock();
  74. }
  75. internal OpcodeBlock Compile(XPathExpr expr)
  76. {
  77. this.codeBlock = new OpcodeBlock(); // struct
  78. this.CompileExpression(expr);
  79. return this.codeBlock;
  80. }
  81. OpcodeBlock CompileBlock(XPathExpr expr)
  82. {
  83. XPathExprCompiler compiler = new XPathExprCompiler(this);
  84. return compiler.Compile(expr);
  85. }
  86. void CompileBoolean(XPathExpr expr, bool testValue)
  87. {
  88. // Boolean expressions must always have at least 2 sub expressions
  89. Fx.Assert(expr.SubExprCount > 1, "");
  90. if (this.compiler.nestingLevel == 1)
  91. {
  92. this.CompileBasicBoolean(expr, testValue);
  93. return;
  94. }
  95. OpcodeBlock boolBlock = new OpcodeBlock(); // struct
  96. Opcode blockEnd = new BlockEndOpcode();
  97. // Set up the result mask
  98. boolBlock.Append(new PushBooleanOpcode(testValue));
  99. XPathExprList subExprList = expr.SubExpr;
  100. XPathExpr subExpr;
  101. // the first expression needs the least work..
  102. subExpr = subExprList[0];
  103. boolBlock.Append(this.CompileBlock(subExpr));
  104. if (subExpr.ReturnType != ValueDataType.Boolean)
  105. {
  106. boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean));
  107. }
  108. boolBlock.Append(new ApplyBooleanOpcode(blockEnd, testValue));
  109. // Compile remaining sub-expressions
  110. for (int i = 1; i < subExprList.Count; ++i)
  111. {
  112. subExpr = subExprList[i];
  113. boolBlock.Append(new StartBooleanOpcode(testValue));
  114. boolBlock.Append(this.CompileBlock(subExpr));
  115. // Make sure each sub-expression can produce a boolean result
  116. if (subExpr.ReturnType != ValueDataType.Boolean)
  117. {
  118. boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean));
  119. }
  120. boolBlock.Append(new EndBooleanOpcode(blockEnd, testValue));
  121. }
  122. boolBlock.Append(blockEnd);
  123. this.codeBlock.Append(boolBlock);
  124. }
  125. // Compiles expressions at nesting level == 1 -> boolean expressions that can be processed
  126. // with less complex opcodes because they will never track multiple sequences simultaneously
  127. void CompileBasicBoolean(XPathExpr expr, bool testValue)
  128. {
  129. // Boolean expressions must always have at least 2 sub expressions
  130. Fx.Assert(expr.SubExprCount > 1, "");
  131. Fx.Assert(this.compiler.nestingLevel == 1, "");
  132. OpcodeBlock boolBlock = new OpcodeBlock(); // struct
  133. Opcode blockEnd = new BlockEndOpcode();
  134. XPathExprList subExprList = expr.SubExpr;
  135. // Compile sub-expressions
  136. for (int i = 0; i < subExprList.Count; ++i)
  137. {
  138. XPathExpr subExpr = subExprList[i];
  139. boolBlock.Append(this.CompileBlock(subExpr));
  140. // Make sure each sub-expression can produce a boolean result
  141. if (subExpr.ReturnType != ValueDataType.Boolean)
  142. {
  143. boolBlock.Append(new TypecastOpcode(ValueDataType.Boolean));
  144. }
  145. if (i < (subExprList.Count - 1))
  146. {
  147. // No point jumping if this is the last expression
  148. boolBlock.Append(new JumpIfOpcode(blockEnd, testValue));
  149. }
  150. }
  151. boolBlock.Append(blockEnd);
  152. this.codeBlock.Append(boolBlock);
  153. }
  154. void CompileExpression(XPathExpr expr)
  155. {
  156. Fx.Assert(null != expr, "");
  157. switch (expr.Type)
  158. {
  159. default:
  160. this.ThrowError(QueryCompileError.UnsupportedExpression);
  161. break;
  162. case XPathExprType.And:
  163. this.CompileBoolean(expr, true);
  164. break;
  165. case XPathExprType.Or:
  166. this.CompileBoolean(expr, false);
  167. break;
  168. case XPathExprType.Relational:
  169. this.CompileRelational((XPathRelationExpr)expr);
  170. break;
  171. case XPathExprType.Function:
  172. this.CompileFunction((XPathFunctionExpr)expr);
  173. break;
  174. case XPathExprType.Union:
  175. {
  176. XPathConjunctExpr unionExpr = (XPathConjunctExpr)expr;
  177. this.CompileExpression(unionExpr.Left);
  178. this.CompileExpression(unionExpr.Right);
  179. this.codeBlock.Append(new UnionOpcode());
  180. }
  181. break;
  182. case XPathExprType.RelativePath:
  183. this.CompileRelativePath(expr, true);
  184. break;
  185. case XPathExprType.LocationPath:
  186. if (expr.SubExprCount > 0)
  187. {
  188. this.CompileLocationPath(expr);
  189. // Step complete. Transfer results onto the value stack
  190. this.codeBlock.Append(new PopSequenceToValueStackOpcode());
  191. }
  192. break;
  193. case XPathExprType.Math:
  194. this.CompileMath((XPathMathExpr)expr);
  195. break;
  196. case XPathExprType.Number:
  197. XPathNumberExpr number = (XPathNumberExpr)expr;
  198. double literal = number.Number;
  199. if (number.Negate)
  200. {
  201. number.Negate = false;
  202. literal = -literal;
  203. }
  204. this.codeBlock.Append(new PushNumberOpcode(literal));
  205. break;
  206. case XPathExprType.String:
  207. this.codeBlock.Append(new PushStringOpcode(((XPathStringExpr)expr).String));
  208. break;
  209. case XPathExprType.Filter:
  210. this.CompileFilter(expr);
  211. if (expr.ReturnType == ValueDataType.Sequence)
  212. {
  213. this.codeBlock.Append(new PopSequenceToValueStackOpcode());
  214. }
  215. break;
  216. case XPathExprType.Path:
  217. this.CompilePath(expr);
  218. if (expr.SubExprCount == 0 && expr.ReturnType == ValueDataType.Sequence)
  219. {
  220. this.codeBlock.Append(new PopSequenceToValueStackOpcode());
  221. }
  222. break;
  223. case XPathExprType.XsltFunction:
  224. this.CompileXsltFunction((XPathXsltFunctionExpr)expr);
  225. break;
  226. case XPathExprType.XsltVariable:
  227. this.CompileXsltVariable((XPathXsltVariableExpr)expr);
  228. break;
  229. }
  230. NegateIfRequired(expr);
  231. }
  232. void CompileFilter(XPathExpr expr)
  233. {
  234. Fx.Assert(XPathExprType.Filter == expr.Type, "");
  235. // The filter expression has two components - the expression and its predicate
  236. // It may have an optional relative path following it
  237. //Debug.Assert(expr.SubExprCount <= 3);
  238. XPathExprList subExpr = expr.SubExpr;
  239. XPathExpr filterExpr = subExpr[0];
  240. if (subExpr.Count > 1 && ValueDataType.Sequence != filterExpr.ReturnType)
  241. {
  242. this.ThrowError(QueryCompileError.InvalidExpression);
  243. }
  244. // The filter expression will return a sequence and push it onto the value stack
  245. // Transfer it back to the sequence stack, so we can keep working on it
  246. this.CompileExpression(filterExpr);
  247. if (filterExpr.ReturnType == ValueDataType.Sequence)
  248. {
  249. if (!IsSpecialInternalFunction(filterExpr) && expr.SubExprCount > 1)
  250. {
  251. // Flatten the sequence and move it to the sequence stack
  252. this.codeBlock.Append(new MergeOpcode());
  253. this.codeBlock.Append(new PopSequenceToSequenceStackOpcode());
  254. }
  255. else if (IsSpecialInternalFunction(filterExpr) && expr.SubExprCount > 1)
  256. {
  257. this.codeBlock.DetachLast();
  258. }
  259. // Now, apply the predicates
  260. this.compiler.nestingLevel++;
  261. if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ]
  262. {
  263. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep));
  264. }
  265. for (int i = 1; i < expr.SubExprCount; ++i)
  266. {
  267. this.CompilePredicate(subExpr[i]);
  268. }
  269. this.compiler.nestingLevel--;
  270. }
  271. }
  272. bool IsSpecialInternalFunction(XPathExpr expr)
  273. {
  274. if (expr.Type != XPathExprType.XsltFunction)
  275. {
  276. return false;
  277. }
  278. XPathMessageFunction func = ((XPathXsltFunctionExpr)expr).Function as XPathMessageFunction;
  279. if (func != null)
  280. {
  281. return func.ReturnType == XPathResultType.NodeSet && func.Maxargs == 0;
  282. }
  283. return false;
  284. }
  285. void CompileFunction(XPathFunctionExpr expr)
  286. {
  287. // In some scenarios, some functions are handled in a special way
  288. if (this.CompileFunctionSpecial(expr))
  289. {
  290. return;
  291. }
  292. // Generic function compilation
  293. QueryFunction function = expr.Function;
  294. // Compile each argument expression first, introducing a typecast where appropriate
  295. // Arguments are pushed C style - right to left
  296. if (expr.SubExprCount > 0)
  297. {
  298. XPathExprList paramList = expr.SubExpr;
  299. for (int i = paramList.Count - 1; i >= 0; --i)
  300. {
  301. this.CompileFunctionParam(function, expr.SubExpr, i);
  302. }
  303. }
  304. this.codeBlock.Append(new FunctionCallOpcode(function));
  305. if (1 == this.compiler.nestingLevel && function.TestFlag(QueryFunctionFlag.UsesContextNode))
  306. {
  307. this.compiler.SetPushInitialContext(true);
  308. }
  309. }
  310. void CompileFunctionParam(QueryFunction function, XPathExprList paramList, int index)
  311. {
  312. XPathExpr param = paramList[index];
  313. this.CompileExpression(param);
  314. if (ValueDataType.None != function.ParamTypes[index])
  315. {
  316. if (param.ReturnType != function.ParamTypes[index])
  317. {
  318. if (function.ParamTypes[index] == ValueDataType.Sequence)
  319. {
  320. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.InvalidTypeConversion));
  321. }
  322. this.CompileTypecast(function.ParamTypes[index]);
  323. }
  324. }
  325. }
  326. // Some functions are compiled with special opcodes to optimize perf in special situations
  327. // 1. starts-with(string, literal)
  328. bool CompileFunctionSpecial(XPathFunctionExpr expr)
  329. {
  330. XPathFunction function = expr.Function as XPathFunction;
  331. if (null != function)
  332. {
  333. if (XPathFunctionID.StartsWith == function.ID)
  334. {
  335. // Does the 2nd parameter start with a string literal? Use a special opcode to handle those..
  336. Fx.Assert(expr.SubExprCount == 2, "");
  337. if (XPathExprType.String == expr.SubExpr[1].Type)
  338. {
  339. this.CompileFunctionParam(function, expr.SubExpr, 0);
  340. this.codeBlock.Append(new StringPrefixOpcode(((XPathStringExpr)expr.SubExpr[1]).String));
  341. return true;
  342. }
  343. }
  344. }
  345. return false;
  346. }
  347. void CompileLiteralRelation(XPathRelationExpr expr)
  348. {
  349. XPathLiteralExpr left = (XPathLiteralExpr)expr.Left;
  350. XPathLiteralExpr right = (XPathLiteralExpr)expr.Right;
  351. bool result = QueryValueModel.CompileTimeCompare(left.Literal, right.Literal, expr.Op);
  352. this.codeBlock.Append(new PushBooleanOpcode(result));
  353. }
  354. void CompileLiteralOrdinal(XPathExpr expr)
  355. {
  356. int ordinal = 0;
  357. try
  358. {
  359. XPathNumberExpr numExpr = (XPathNumberExpr)expr;
  360. ordinal = Convert.ToInt32(numExpr.Number);
  361. if (numExpr.Negate)
  362. {
  363. ordinal = -ordinal;
  364. numExpr.Negate = false;
  365. }
  366. if (ordinal < 1)
  367. {
  368. this.ThrowError(QueryCompileError.InvalidOrdinal);
  369. }
  370. }
  371. catch (OverflowException)
  372. {
  373. this.ThrowError(QueryCompileError.InvalidOrdinal);
  374. }
  375. if (0 != (this.compiler.flags & QueryCompilerFlags.InverseQuery))
  376. {
  377. this.codeBlock.Append(new PushContextPositionOpcode());
  378. this.codeBlock.Append(new NumberEqualsOpcode(ordinal));
  379. }
  380. else
  381. {
  382. this.codeBlock.Append(new LiteralOrdinalOpcode(ordinal));
  383. }
  384. }
  385. void CompileLocationPath(XPathExpr expr)
  386. {
  387. Fx.Assert(expr.SubExprCount > 0, "");
  388. XPathStepExpr firstStep = (XPathStepExpr)expr.SubExpr[0];
  389. this.CompileSteps(expr.SubExpr);
  390. if (1 == this.compiler.nestingLevel)
  391. {
  392. this.compiler.SetPushInitialContext(firstStep.SelectDesc.Type != QueryNodeType.Root);
  393. }
  394. }
  395. void CompileMath(XPathMathExpr mathExpr)
  396. {
  397. // are we doing math on two literal numbers? If so, do it at compile time
  398. if (XPathExprType.Number == mathExpr.Right.Type && XPathExprType.Number == mathExpr.Left.Type)
  399. {
  400. double left = ((XPathNumberExpr)mathExpr.Left).Number;
  401. if (((XPathNumberExpr)mathExpr.Left).Negate)
  402. {
  403. ((XPathNumberExpr)mathExpr.Left).Negate = false;
  404. left = -left;
  405. }
  406. double right = ((XPathNumberExpr)mathExpr.Right).Number;
  407. if (((XPathNumberExpr)mathExpr.Right).Negate)
  408. {
  409. ((XPathNumberExpr)mathExpr.Right).Negate = false;
  410. right = -right;
  411. }
  412. switch (mathExpr.Op)
  413. {
  414. case MathOperator.Div:
  415. left /= right;
  416. break;
  417. case MathOperator.Minus:
  418. left -= right;
  419. break;
  420. case MathOperator.Mod:
  421. left %= right;
  422. break;
  423. case MathOperator.Multiply:
  424. left *= right;
  425. break;
  426. case MathOperator.Plus:
  427. left += right;
  428. break;
  429. }
  430. this.codeBlock.Append(new PushNumberOpcode(left));
  431. return;
  432. }
  433. // Arguments are pushed C style - right to left
  434. this.CompileExpression(mathExpr.Right);
  435. if (ValueDataType.Double != mathExpr.Right.ReturnType)
  436. {
  437. this.CompileTypecast(ValueDataType.Double);
  438. }
  439. this.CompileExpression(mathExpr.Left);
  440. if (ValueDataType.Double != mathExpr.Left.ReturnType)
  441. {
  442. this.CompileTypecast(ValueDataType.Double);
  443. }
  444. this.codeBlock.Append(this.CreateMathOpcode(mathExpr.Op));
  445. }
  446. void CompileNumberLiteralEquality(XPathRelationExpr expr)
  447. {
  448. Fx.Assert(expr.Op == RelationOperator.Eq, "");
  449. bool leftNumber = (XPathExprType.Number == expr.Left.Type);
  450. bool rightNumber = (XPathExprType.Number == expr.Right.Type);
  451. Fx.Assert(leftNumber || rightNumber, "");
  452. Fx.Assert(!(leftNumber && rightNumber), "");
  453. this.CompileExpression(leftNumber ? expr.Right : expr.Left);
  454. XPathNumberExpr litExpr = leftNumber ? (XPathNumberExpr)expr.Left : (XPathNumberExpr)expr.Right;
  455. double literal = litExpr.Number;
  456. if (litExpr.Negate)
  457. {
  458. litExpr.Negate = false;
  459. literal = -literal;
  460. }
  461. this.codeBlock.Append(new NumberEqualsOpcode(literal));
  462. }
  463. void CompileNumberRelation(XPathRelationExpr expr)
  464. {
  465. if (expr.Op == RelationOperator.Eq)
  466. {
  467. this.CompileNumberLiteralEquality(expr);
  468. return;
  469. }
  470. bool leftNumber = (XPathExprType.Number == expr.Left.Type);
  471. bool rightNumber = (XPathExprType.Number == expr.Right.Type);
  472. Fx.Assert(leftNumber || rightNumber, "");
  473. Fx.Assert(!(leftNumber && rightNumber), "");
  474. this.CompileExpression(leftNumber ? expr.Right : expr.Left);
  475. XPathNumberExpr litExpr = leftNumber ? (XPathNumberExpr)expr.Left : (XPathNumberExpr)expr.Right;
  476. double literal = litExpr.Number;
  477. if (litExpr.Negate)
  478. {
  479. litExpr.Negate = false;
  480. literal = -literal;
  481. }
  482. // To maximize code branch commonality, we canonacalize the relation expressions so that the non-literal
  483. // is always to the left and the literal to the right. If this makes us swap expressions, we must also flip
  484. // relation operators appropriately.
  485. if (leftNumber)
  486. {
  487. // Flip operators
  488. switch (expr.Op)
  489. {
  490. case RelationOperator.Gt:
  491. expr.Op = RelationOperator.Lt;
  492. break;
  493. case RelationOperator.Ge:
  494. expr.Op = RelationOperator.Le;
  495. break;
  496. case RelationOperator.Lt:
  497. expr.Op = RelationOperator.Gt;
  498. break;
  499. case RelationOperator.Le:
  500. expr.Op = RelationOperator.Ge;
  501. break;
  502. }
  503. }
  504. if (0 != (this.compiler.flags & QueryCompilerFlags.InverseQuery))
  505. {
  506. this.codeBlock.Append(new NumberIntervalOpcode(literal, expr.Op));
  507. }
  508. else
  509. {
  510. this.codeBlock.Append(new NumberRelationOpcode(literal, expr.Op));
  511. }
  512. }
  513. void CompilePath(XPathExpr expr)
  514. {
  515. Fx.Assert(expr.SubExprCount == 2 || expr.SubExprCount == 3, "");
  516. if (expr.Type == XPathExprType.Filter)
  517. {
  518. this.CompileFilter(expr.SubExpr[0]);
  519. }
  520. else
  521. {
  522. this.CompileExpression(expr.SubExpr[0]);
  523. if (expr.SubExpr[0].ReturnType == ValueDataType.Sequence)
  524. {
  525. if (IsSpecialInternalFunction(expr.SubExpr[0]))
  526. {
  527. this.codeBlock.DetachLast();
  528. }
  529. else
  530. {
  531. this.codeBlock.Append(new MergeOpcode());
  532. this.codeBlock.Append(new PopSequenceToSequenceStackOpcode());
  533. }
  534. }
  535. }
  536. if (expr.SubExprCount == 2)
  537. {
  538. this.CompileRelativePath(expr.SubExpr[1], false);
  539. }
  540. else if (expr.SubExprCount == 3)
  541. {
  542. // Compile the step
  543. XPathExpr e = expr.SubExpr[1];
  544. Fx.Assert(XPathExprType.PathStep == e.Type, "");
  545. XPathStepExpr step = (XPathStepExpr)e;
  546. Fx.Assert(QueryNodeType.Root != step.SelectDesc.Type, "");
  547. if (!step.SelectDesc.Axis.IsSupported())
  548. {
  549. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.UnsupportedAxis));
  550. }
  551. this.codeBlock.Append(new SelectOpcode(step.SelectDesc));
  552. // The step may have predicates..
  553. if (step.SubExprCount > 0)
  554. {
  555. this.compiler.nestingLevel++;
  556. if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ]
  557. {
  558. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep));
  559. }
  560. this.CompilePredicates(step.SubExpr);
  561. this.compiler.nestingLevel--;
  562. }
  563. // Compile the relative path
  564. this.CompileRelativePath(expr.SubExpr[2], false);
  565. }
  566. }
  567. void CompilePredicate(XPathExpr expr)
  568. {
  569. // If the expression does not return a boolean, introduce a typecast
  570. // If the predicate expression is a standalone number literal, interpret it as a literal
  571. if (expr.IsLiteral && XPathExprType.Number == expr.Type)
  572. {
  573. this.CompileLiteralOrdinal(expr);
  574. }
  575. else
  576. {
  577. this.CompileExpression(expr);
  578. if (expr.ReturnType == ValueDataType.Double)
  579. {
  580. this.codeBlock.Append(new OrdinalOpcode());
  581. }
  582. else if (expr.ReturnType != ValueDataType.Boolean)
  583. {
  584. this.CompileTypecast(ValueDataType.Boolean);
  585. }
  586. }
  587. // Apply the results of the predicate on the context sequence
  588. this.codeBlock.Append(new ApplyFilterOpcode());
  589. }
  590. void CompilePredicates(XPathExprList exprList)
  591. {
  592. // Compile each predicate expression first
  593. for (int i = 0; i < exprList.Count; ++i)
  594. {
  595. this.CompilePredicate(exprList[i]);
  596. }
  597. }
  598. void CompileRelational(XPathRelationExpr expr)
  599. {
  600. // Are we comparing two literals?
  601. if (expr.Left.IsLiteral && expr.Right.IsLiteral)
  602. {
  603. // Do the comparison at compile time
  604. this.CompileLiteralRelation(expr);
  605. return;
  606. }
  607. // != is not optimized in M5
  608. if (expr.Op != RelationOperator.Ne)
  609. {
  610. // Number relations are handled in a special way
  611. if (XPathExprType.Number == expr.Left.Type || XPathExprType.Number == expr.Right.Type)
  612. {
  613. this.CompileNumberRelation(expr);
  614. return;
  615. }
  616. // Equality tests with string literals are handled in a special way
  617. if (expr.Op == RelationOperator.Eq && (XPathExprType.String == expr.Left.Type || XPathExprType.String == expr.Right.Type))
  618. {
  619. this.CompileStringLiteralEquality(expr);
  620. return;
  621. }
  622. }
  623. // Can't optimize. Use a general purpose relation opcode
  624. this.CompileExpression(expr.Left);
  625. this.CompileExpression(expr.Right);
  626. this.codeBlock.Append(new RelationOpcode(expr.Op));
  627. }
  628. void CompileRelativePath(XPathExpr expr, bool start)
  629. {
  630. Fx.Assert(XPathExprType.RelativePath == expr.Type, "");
  631. this.CompileSteps(expr.SubExpr, start);
  632. // Step complete. Transfer results onto the value stack
  633. this.codeBlock.Append(new PopSequenceToValueStackOpcode());
  634. }
  635. void CompileStringLiteralEquality(XPathRelationExpr expr)
  636. {
  637. Fx.Assert(expr.Op == RelationOperator.Eq, "");
  638. bool leftString = (XPathExprType.String == expr.Left.Type);
  639. bool rightString = (XPathExprType.String == expr.Right.Type);
  640. Fx.Assert(leftString || rightString, "");
  641. Fx.Assert(!(leftString && rightString), "");
  642. this.CompileExpression(leftString ? expr.Right : expr.Left);
  643. string literal = leftString ? ((XPathStringExpr)expr.Left).String : ((XPathStringExpr)expr.Right).String;
  644. this.codeBlock.Append(new StringEqualsOpcode(literal));
  645. }
  646. void CompileSteps(XPathExprList steps)
  647. {
  648. CompileSteps(steps, true);
  649. }
  650. void CompileSteps(XPathExprList steps, bool start)
  651. {
  652. for (int i = 0; i < steps.Count; ++i)
  653. {
  654. Fx.Assert(XPathExprType.PathStep == steps[i].Type, "");
  655. XPathStepExpr step = (XPathStepExpr)steps[i];
  656. if (!step.SelectDesc.Axis.IsSupported())
  657. {
  658. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.UnsupportedAxis));
  659. }
  660. Opcode stepOpcode = null;
  661. if (start && 0 == i)
  662. {
  663. // First steps
  664. // Is this an absolute path? We have an absolute path if the first step selects the root
  665. if (QueryNodeType.Root == step.SelectDesc.Type)
  666. {
  667. stepOpcode = new SelectRootOpcode();
  668. }
  669. else
  670. {
  671. stepOpcode = new InitialSelectOpcode(step.SelectDesc);
  672. }
  673. }
  674. else
  675. {
  676. Fx.Assert(QueryNodeType.Root != step.SelectDesc.Type, "");
  677. stepOpcode = new SelectOpcode(step.SelectDesc);
  678. }
  679. this.codeBlock.Append(stepOpcode);
  680. // The step may have predicates..
  681. if (step.SubExprCount > 0)
  682. {
  683. this.compiler.nestingLevel++;
  684. if (this.compiler.nestingLevel > 3) // throw if we find something deepter than [ [ ] ]
  685. {
  686. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.PredicateNestingTooDeep));
  687. }
  688. this.CompilePredicates(step.SubExpr);
  689. this.compiler.nestingLevel--;
  690. }
  691. }
  692. }
  693. void CompileTypecast(ValueDataType destType)
  694. {
  695. Fx.Assert(ValueDataType.None != destType, "");
  696. this.codeBlock.Append(new TypecastOpcode(destType));
  697. }
  698. void CompileXsltFunction(XPathXsltFunctionExpr expr)
  699. {
  700. // Compile each argument expression first, introducing a typecast where appropriate
  701. // Arguments are pushed C style - right to left
  702. if (expr.SubExprCount > 0)
  703. {
  704. XPathExprList paramList = expr.SubExpr;
  705. for (int i = paramList.Count - 1; i >= 0; --i)
  706. {
  707. XPathExpr param = paramList[i];
  708. this.CompileExpression(param);
  709. ValueDataType paramType = XPathXsltFunctionExpr.ConvertTypeFromXslt(expr.Function.ArgTypes[i]);
  710. if (ValueDataType.None != paramType)
  711. {
  712. if (param.ReturnType != paramType)
  713. {
  714. this.CompileTypecast(paramType);
  715. }
  716. }
  717. }
  718. }
  719. if (expr.Function is XPathMessageFunction)
  720. {
  721. this.codeBlock.Append(new XPathMessageFunctionCallOpcode((XPathMessageFunction)expr.Function, expr.SubExprCount));
  722. if (IsSpecialInternalFunction(expr))
  723. {
  724. this.codeBlock.Append(new PopSequenceToValueStackOpcode());
  725. }
  726. }
  727. else
  728. {
  729. this.codeBlock.Append(new XsltFunctionCallOpcode(expr.Context, expr.Function, expr.SubExprCount));
  730. }
  731. }
  732. void CompileXsltVariable(XPathXsltVariableExpr expr)
  733. {
  734. #if NO
  735. // Remove this block if we never decide to use variables in an XPathMessageContext
  736. // It is here in case we decide to
  737. if (expr.Variable is XPathMessageVariable)
  738. {
  739. this.codeBlock.Append(new PushXPathMessageVariableOpcode((XPathMessageVariable)expr.Variable));
  740. }
  741. else
  742. {
  743. this.codeBlock.Append(new PushXsltVariableOpcode(expr.Context, expr.Variable));
  744. }
  745. #endif
  746. this.codeBlock.Append(new PushXsltVariableOpcode(expr.Context, expr.Variable));
  747. }
  748. MathOpcode CreateMathOpcode(MathOperator op)
  749. {
  750. MathOpcode opcode = null;
  751. switch (op)
  752. {
  753. case MathOperator.None:
  754. Fx.Assert("");
  755. break;
  756. case MathOperator.Plus:
  757. opcode = new PlusOpcode();
  758. break;
  759. case MathOperator.Minus:
  760. opcode = new MinusOpcode();
  761. break;
  762. case MathOperator.Div:
  763. opcode = new DivideOpcode();
  764. break;
  765. case MathOperator.Multiply:
  766. opcode = new MultiplyOpcode();
  767. break;
  768. case MathOperator.Mod:
  769. opcode = new ModulusOpcode();
  770. break;
  771. case MathOperator.Negate:
  772. opcode = new NegateOpcode();
  773. break;
  774. }
  775. return opcode;
  776. }
  777. void NegateIfRequired(XPathExpr expr)
  778. {
  779. // We can combine these two since the flags they examine are set in exactly one (the same) place.
  780. TypecastIfRequired(expr);
  781. if (expr.Negate)
  782. {
  783. expr.Negate = false;
  784. this.codeBlock.Append(new NegateOpcode());
  785. }
  786. }
  787. void TypecastIfRequired(XPathExpr expr)
  788. {
  789. if (expr.TypecastRequired)
  790. {
  791. expr.TypecastRequired = false;
  792. CompileTypecast(expr.ReturnType);
  793. }
  794. }
  795. void ThrowError(QueryCompileError error)
  796. {
  797. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(error));
  798. }
  799. }
  800. }
  801. }