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