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