dom/xslt/xpath/txXPathOptimizer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "txXPathOptimizer.h"
michael@0 7 #include "txExprResult.h"
michael@0 8 #include "nsIAtom.h"
michael@0 9 #include "nsGkAtoms.h"
michael@0 10 #include "txXPathNode.h"
michael@0 11 #include "txExpr.h"
michael@0 12 #include "txIXPathContext.h"
michael@0 13
michael@0 14 class txEarlyEvalContext : public txIEvalContext
michael@0 15 {
michael@0 16 public:
michael@0 17 txEarlyEvalContext(txResultRecycler* aRecycler)
michael@0 18 : mRecycler(aRecycler)
michael@0 19 {
michael@0 20 }
michael@0 21
michael@0 22 // txIEvalContext
michael@0 23 nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
michael@0 24 txAExprResult*& aResult)
michael@0 25 {
michael@0 26 NS_NOTREACHED("shouldn't depend on this context");
michael@0 27 return NS_ERROR_FAILURE;
michael@0 28 }
michael@0 29 bool isStripSpaceAllowed(const txXPathNode& aNode)
michael@0 30 {
michael@0 31 NS_NOTREACHED("shouldn't depend on this context");
michael@0 32 return false;
michael@0 33 }
michael@0 34 void* getPrivateContext()
michael@0 35 {
michael@0 36 NS_NOTREACHED("shouldn't depend on this context");
michael@0 37 return nullptr;
michael@0 38 }
michael@0 39 txResultRecycler* recycler()
michael@0 40 {
michael@0 41 return mRecycler;
michael@0 42 }
michael@0 43 void receiveError(const nsAString& aMsg, nsresult aRes)
michael@0 44 {
michael@0 45 }
michael@0 46 const txXPathNode& getContextNode()
michael@0 47 {
michael@0 48 NS_NOTREACHED("shouldn't depend on this context");
michael@0 49
michael@0 50 // This will return an invalid node, but we should never
michael@0 51 // get here so that's fine.
michael@0 52
michael@0 53 return *static_cast<txXPathNode*>(nullptr);
michael@0 54 }
michael@0 55 uint32_t size()
michael@0 56 {
michael@0 57 NS_NOTREACHED("shouldn't depend on this context");
michael@0 58 return 1;
michael@0 59 }
michael@0 60 uint32_t position()
michael@0 61 {
michael@0 62 NS_NOTREACHED("shouldn't depend on this context");
michael@0 63 return 1;
michael@0 64 }
michael@0 65
michael@0 66 private:
michael@0 67 txResultRecycler* mRecycler;
michael@0 68 };
michael@0 69
michael@0 70
michael@0 71 nsresult
michael@0 72 txXPathOptimizer::optimize(Expr* aInExpr, Expr** aOutExpr)
michael@0 73 {
michael@0 74 *aOutExpr = nullptr;
michael@0 75 nsresult rv = NS_OK;
michael@0 76
michael@0 77 // First check if the expression will produce the same result
michael@0 78 // under any context.
michael@0 79 Expr::ExprType exprType = aInExpr->getType();
michael@0 80 if (exprType != Expr::LITERAL_EXPR &&
michael@0 81 !aInExpr->isSensitiveTo(Expr::ANY_CONTEXT)) {
michael@0 82 nsRefPtr<txResultRecycler> recycler = new txResultRecycler;
michael@0 83 NS_ENSURE_TRUE(recycler, NS_ERROR_OUT_OF_MEMORY);
michael@0 84
michael@0 85 rv = recycler->init();
michael@0 86 NS_ENSURE_SUCCESS(rv, rv);
michael@0 87
michael@0 88 txEarlyEvalContext context(recycler);
michael@0 89 nsRefPtr<txAExprResult> exprRes;
michael@0 90
michael@0 91 // Don't throw if this fails since it could be that the expression
michael@0 92 // is or contains an error-expression.
michael@0 93 rv = aInExpr->evaluate(&context, getter_AddRefs(exprRes));
michael@0 94 if (NS_SUCCEEDED(rv)) {
michael@0 95 *aOutExpr = new txLiteralExpr(exprRes);
michael@0 96 }
michael@0 97
michael@0 98 return NS_OK;
michael@0 99 }
michael@0 100
michael@0 101 // Then optimize sub expressions
michael@0 102 uint32_t i = 0;
michael@0 103 Expr* subExpr;
michael@0 104 while ((subExpr = aInExpr->getSubExprAt(i))) {
michael@0 105 Expr* newExpr = nullptr;
michael@0 106 rv = optimize(subExpr, &newExpr);
michael@0 107 NS_ENSURE_SUCCESS(rv, rv);
michael@0 108 if (newExpr) {
michael@0 109 delete subExpr;
michael@0 110 aInExpr->setSubExprAt(i, newExpr);
michael@0 111 }
michael@0 112
michael@0 113 ++i;
michael@0 114 }
michael@0 115
michael@0 116 // Finally see if current expression can be optimized
michael@0 117 switch (exprType) {
michael@0 118 case Expr::LOCATIONSTEP_EXPR:
michael@0 119 return optimizeStep(aInExpr, aOutExpr);
michael@0 120
michael@0 121 case Expr::PATH_EXPR:
michael@0 122 return optimizePath(aInExpr, aOutExpr);
michael@0 123
michael@0 124 case Expr::UNION_EXPR:
michael@0 125 return optimizeUnion(aInExpr, aOutExpr);
michael@0 126
michael@0 127 default:
michael@0 128 break;
michael@0 129 }
michael@0 130
michael@0 131 return NS_OK;
michael@0 132 }
michael@0 133
michael@0 134 nsresult
michael@0 135 txXPathOptimizer::optimizeStep(Expr* aInExpr, Expr** aOutExpr)
michael@0 136 {
michael@0 137 LocationStep* step = static_cast<LocationStep*>(aInExpr);
michael@0 138
michael@0 139 if (step->getAxisIdentifier() == LocationStep::ATTRIBUTE_AXIS) {
michael@0 140 // Test for @foo type steps.
michael@0 141 txNameTest* nameTest = nullptr;
michael@0 142 if (!step->getSubExprAt(0) &&
michael@0 143 step->getNodeTest()->getType() == txNameTest::NAME_TEST &&
michael@0 144 (nameTest = static_cast<txNameTest*>(step->getNodeTest()))->
michael@0 145 mLocalName != nsGkAtoms::_asterix) {
michael@0 146
michael@0 147 *aOutExpr = new txNamedAttributeStep(nameTest->mNamespace,
michael@0 148 nameTest->mPrefix,
michael@0 149 nameTest->mLocalName);
michael@0 150 NS_ENSURE_TRUE(*aOutExpr, NS_ERROR_OUT_OF_MEMORY);
michael@0 151
michael@0 152 return NS_OK; // return since we no longer have a step-object.
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 // Test for predicates that can be combined into the nodetest
michael@0 157 Expr* pred;
michael@0 158 while ((pred = step->getSubExprAt(0)) &&
michael@0 159 !pred->canReturnType(Expr::NUMBER_RESULT) &&
michael@0 160 !pred->isSensitiveTo(Expr::NODESET_CONTEXT)) {
michael@0 161 txNodeTest* predTest = new txPredicatedNodeTest(step->getNodeTest(), pred);
michael@0 162 NS_ENSURE_TRUE(predTest, NS_ERROR_OUT_OF_MEMORY);
michael@0 163
michael@0 164 step->dropFirst();
michael@0 165 step->setNodeTest(predTest);
michael@0 166 }
michael@0 167
michael@0 168 return NS_OK;
michael@0 169 }
michael@0 170
michael@0 171 nsresult
michael@0 172 txXPathOptimizer::optimizePath(Expr* aInExpr, Expr** aOutExpr)
michael@0 173 {
michael@0 174 PathExpr* path = static_cast<PathExpr*>(aInExpr);
michael@0 175
michael@0 176 uint32_t i;
michael@0 177 Expr* subExpr;
michael@0 178 // look for steps like "//foo" that can be turned into "/descendant::foo"
michael@0 179 // and "//." that can be turned into "/descendant-or-self::node()"
michael@0 180 for (i = 0; (subExpr = path->getSubExprAt(i)); ++i) {
michael@0 181 if (path->getPathOpAt(i) == PathExpr::DESCENDANT_OP &&
michael@0 182 subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
michael@0 183 !subExpr->getSubExprAt(0)) {
michael@0 184 LocationStep* step = static_cast<LocationStep*>(subExpr);
michael@0 185 if (step->getAxisIdentifier() == LocationStep::CHILD_AXIS) {
michael@0 186 step->setAxisIdentifier(LocationStep::DESCENDANT_AXIS);
michael@0 187 path->setPathOpAt(i, PathExpr::RELATIVE_OP);
michael@0 188 }
michael@0 189 else if (step->getAxisIdentifier() == LocationStep::SELF_AXIS) {
michael@0 190 step->setAxisIdentifier(LocationStep::DESCENDANT_OR_SELF_AXIS);
michael@0 191 path->setPathOpAt(i, PathExpr::RELATIVE_OP);
michael@0 192 }
michael@0 193 }
michael@0 194 }
michael@0 195
michael@0 196 // look for expressions that start with a "./"
michael@0 197 subExpr = path->getSubExprAt(0);
michael@0 198 LocationStep* step;
michael@0 199 if (subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
michael@0 200 path->getSubExprAt(1) &&
michael@0 201 path->getPathOpAt(1) != PathExpr::DESCENDANT_OP) {
michael@0 202 step = static_cast<LocationStep*>(subExpr);
michael@0 203 if (step->getAxisIdentifier() == LocationStep::SELF_AXIS &&
michael@0 204 !step->getSubExprAt(0)) {
michael@0 205 txNodeTest* test = step->getNodeTest();
michael@0 206 txNodeTypeTest* typeTest;
michael@0 207 if (test->getType() == txNodeTest::NODETYPE_TEST &&
michael@0 208 (typeTest = static_cast<txNodeTypeTest*>(test))->
michael@0 209 getNodeTestType() == txNodeTypeTest::NODE_TYPE) {
michael@0 210 // We have a '.' as first step followed by a single '/'.
michael@0 211
michael@0 212 // Check if there are only two steps. If so, return the second
michael@0 213 // as resulting expression.
michael@0 214 if (!path->getSubExprAt(2)) {
michael@0 215 *aOutExpr = path->getSubExprAt(1);
michael@0 216 path->setSubExprAt(1, nullptr);
michael@0 217
michael@0 218 return NS_OK;
michael@0 219 }
michael@0 220
michael@0 221 // Just delete the '.' step and leave the rest of the PathExpr
michael@0 222 path->deleteExprAt(0);
michael@0 223 }
michael@0 224 }
michael@0 225 }
michael@0 226
michael@0 227 return NS_OK;
michael@0 228 }
michael@0 229
michael@0 230 nsresult
michael@0 231 txXPathOptimizer::optimizeUnion(Expr* aInExpr, Expr** aOutExpr)
michael@0 232 {
michael@0 233 UnionExpr* uni = static_cast<UnionExpr*>(aInExpr);
michael@0 234
michael@0 235 // Check for expressions like "foo | bar" and
michael@0 236 // "descendant::foo | descendant::bar"
michael@0 237
michael@0 238 nsresult rv;
michael@0 239 uint32_t current;
michael@0 240 Expr* subExpr;
michael@0 241 for (current = 0; (subExpr = uni->getSubExprAt(current)); ++current) {
michael@0 242 if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
michael@0 243 subExpr->getSubExprAt(0)) {
michael@0 244 continue;
michael@0 245 }
michael@0 246
michael@0 247 LocationStep* currentStep = static_cast<LocationStep*>(subExpr);
michael@0 248 LocationStep::LocationStepType axis = currentStep->getAxisIdentifier();
michael@0 249
michael@0 250 txUnionNodeTest* unionTest = nullptr;
michael@0 251
michael@0 252 // Check if there are any other steps with the same axis and merge
michael@0 253 // them with currentStep
michael@0 254 uint32_t i;
michael@0 255 for (i = current + 1; (subExpr = uni->getSubExprAt(i)); ++i) {
michael@0 256 if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
michael@0 257 subExpr->getSubExprAt(0)) {
michael@0 258 continue;
michael@0 259 }
michael@0 260
michael@0 261 LocationStep* step = static_cast<LocationStep*>(subExpr);
michael@0 262 if (step->getAxisIdentifier() != axis) {
michael@0 263 continue;
michael@0 264 }
michael@0 265
michael@0 266 // Create a txUnionNodeTest if needed
michael@0 267 if (!unionTest) {
michael@0 268 nsAutoPtr<txNodeTest> owner(unionTest = new txUnionNodeTest);
michael@0 269 NS_ENSURE_TRUE(unionTest, NS_ERROR_OUT_OF_MEMORY);
michael@0 270
michael@0 271 rv = unionTest->addNodeTest(currentStep->getNodeTest());
michael@0 272 NS_ENSURE_SUCCESS(rv, rv);
michael@0 273
michael@0 274 currentStep->setNodeTest(unionTest);
michael@0 275 owner.forget();
michael@0 276 }
michael@0 277
michael@0 278 // Merge the nodetest into the union
michael@0 279 rv = unionTest->addNodeTest(step->getNodeTest());
michael@0 280 NS_ENSURE_SUCCESS(rv, rv);
michael@0 281
michael@0 282 step->setNodeTest(nullptr);
michael@0 283
michael@0 284 // Remove the step from the UnionExpr
michael@0 285 uni->deleteExprAt(i);
michael@0 286 --i;
michael@0 287 }
michael@0 288
michael@0 289 // Check if all expressions were merged into a single step. If so,
michael@0 290 // return the step as the new expression.
michael@0 291 if (unionTest && current == 0 && !uni->getSubExprAt(1)) {
michael@0 292 // Make sure the step doesn't get deleted when the UnionExpr is
michael@0 293 uni->setSubExprAt(0, nullptr);
michael@0 294 *aOutExpr = currentStep;
michael@0 295
michael@0 296 // Return right away since we no longer have a union
michael@0 297 return NS_OK;
michael@0 298 }
michael@0 299 }
michael@0 300
michael@0 301 return NS_OK;
michael@0 302 }

mercurial