1 /*
2  * reserved comment block
3  * DO NOT REMOVE OR ALTER!
4  */
5 /*
6  * Copyright 2001-2005 The Apache Software Foundation.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 /*
21  * $Id: Step.java,v 1.6 2006/06/06 22:34:34 spericas Exp $
22  */
23 
24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
25 
26 import java.util.Vector;
27 
28 import com.sun.org.apache.bcel.internal.generic.ALOAD;
29 import com.sun.org.apache.bcel.internal.generic.ASTORE;
30 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
31 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
32 import com.sun.org.apache.bcel.internal.generic.ICONST;
33 import com.sun.org.apache.bcel.internal.generic.ILOAD;
34 import com.sun.org.apache.bcel.internal.generic.ISTORE;
35 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
36 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
37 import com.sun.org.apache.bcel.internal.generic.InstructionList;
38 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
39 import com.sun.org.apache.bcel.internal.generic.NEW;
40 import com.sun.org.apache.bcel.internal.generic.PUSH;
41 import com.sun.org.apache.xalan.internal.xsltc.DOM;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
47 import com.sun.org.apache.xml.internal.dtm.Axis;
48 import com.sun.org.apache.xml.internal.dtm.DTM;
49 
50 /**
51  * @author Jacek Ambroziak
52  * @author Santiago Pericas-Geertsen
53  * @author Morten Jorgensen
54  */
55 final class Step extends RelativeLocationPath {
56 
57     /**
58      * This step's axis as defined in class Axis.
59      */
60     private int _axis;
61 
62     /**
63      * A vector of predicates (filters) defined on this step - may be null
64      */
65     private Vector _predicates;
66 
67     /**
68      * Some simple predicates can be handled by this class (and not by the
69      * Predicate class) and will be removed from the above vector as they are
70      * handled. We use this boolean to remember if we did have any predicates.
71      */
72     private boolean _hadPredicates = false;
73 
74     /**
75      * Type of the node test.
76      */
77     private int _nodeType;
78 
Step(int axis, int nodeType, Vector predicates)79     public Step(int axis, int nodeType, Vector predicates) {
80         _axis = axis;
81         _nodeType = nodeType;
82         _predicates = predicates;
83     }
84 
85     /**
86      * Set the parser for this element and all child predicates
87      */
setParser(Parser parser)88     public void setParser(Parser parser) {
89         super.setParser(parser);
90         if (_predicates != null) {
91             final int n = _predicates.size();
92             for (int i = 0; i < n; i++) {
93                 final Predicate exp = (Predicate)_predicates.elementAt(i);
94                 exp.setParser(parser);
95                 exp.setParent(this);
96             }
97         }
98     }
99 
100     /**
101      * Define the axis (defined in Axis class) for this step
102      */
getAxis()103     public int getAxis() {
104         return _axis;
105     }
106 
107     /**
108      * Get the axis (defined in Axis class) for this step
109      */
setAxis(int axis)110     public void setAxis(int axis) {
111         _axis = axis;
112     }
113 
114     /**
115      * Returns the node-type for this step
116      */
getNodeType()117     public int getNodeType() {
118         return _nodeType;
119     }
120 
121     /**
122      * Returns the vector containing all predicates for this step.
123      */
getPredicates()124     public Vector getPredicates() {
125         return _predicates;
126     }
127 
128     /**
129      * Returns the vector containing all predicates for this step.
130      */
addPredicates(Vector predicates)131     public void addPredicates(Vector predicates) {
132         if (_predicates == null) {
133             _predicates = predicates;
134         }
135         else {
136             _predicates.addAll(predicates);
137         }
138     }
139 
140     /**
141      * Returns 'true' if this step has a parent pattern.
142      * This method will return 'false' if this step occurs on its own under
143      * an element like <xsl:for-each> or <xsl:apply-templates>.
144      */
hasParentPattern()145     private boolean hasParentPattern() {
146         final SyntaxTreeNode parent = getParent();
147         return (parent instanceof ParentPattern ||
148                 parent instanceof ParentLocationPath ||
149                 parent instanceof UnionPathExpr ||
150                 parent instanceof FilterParentPath);
151     }
152 
153     /**
154      * Returns 'true' if this step has a parent location path.
155      */
hasParentLocationPath()156     private boolean hasParentLocationPath() {
157         return getParent() instanceof ParentLocationPath;
158     }
159 
160     /**
161      * Returns 'true' if this step has any predicates
162      */
hasPredicates()163     private boolean hasPredicates() {
164         return _predicates != null && _predicates.size() > 0;
165     }
166 
167     /**
168      * Returns 'true' if this step is used within a predicate
169      */
isPredicate()170     private boolean isPredicate() {
171         SyntaxTreeNode parent = this;
172         while (parent != null) {
173             parent = parent.getParent();
174             if (parent instanceof Predicate) return true;
175         }
176         return false;
177     }
178 
179     /**
180      * True if this step is the abbreviated step '.'
181      */
isAbbreviatedDot()182     public boolean isAbbreviatedDot() {
183         return _nodeType == NodeTest.ANODE && _axis == Axis.SELF;
184     }
185 
186 
187     /**
188      * True if this step is the abbreviated step '..'
189      */
isAbbreviatedDDot()190     public boolean isAbbreviatedDDot() {
191         return _nodeType == NodeTest.ANODE && _axis == Axis.PARENT;
192     }
193 
194     /**
195      * Type check this step. The abbreviated steps '.' and '@attr' are
196      * assigned type node if they have no predicates. All other steps
197      * have type node-set.
198      */
typeCheck(SymbolTable stable)199     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
200 
201         // Save this value for later - important for testing for special
202         // combinations of steps and patterns than can be optimised
203         _hadPredicates = hasPredicates();
204 
205         // Special case for '.'
206         //   in the case where '.' has a context such as book/.
207         //   or .[false()] we can not optimize the nodeset to a single node.
208         if (isAbbreviatedDot()) {
209             _type = (hasParentPattern() || hasPredicates() || hasParentLocationPath()) ?
210                 Type.NodeSet : Type.Node;
211         }
212         else {
213             _type = Type.NodeSet;
214         }
215 
216         // Type check all predicates (expressions applied to the step)
217         if (_predicates != null) {
218             final int n = _predicates.size();
219             for (int i = 0; i < n; i++) {
220                 final Expression pred = (Expression)_predicates.elementAt(i);
221                 pred.typeCheck(stable);
222             }
223         }
224 
225         // Return either Type.Node or Type.NodeSet
226         return _type;
227     }
228 
229     /**
230      * Translate a step by pushing the appropriate iterator onto the stack.
231      * The abbreviated steps '.' and '@attr' do not create new iterators
232      * if they are not part of a LocationPath and have no filters.
233      * In these cases a node index instead of an iterator is pushed
234      * onto the stack.
235      */
translate(ClassGenerator classGen, MethodGenerator methodGen)236     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
237         translateStep(classGen, methodGen, hasPredicates() ? _predicates.size() - 1 : -1);
238     }
239 
translateStep(ClassGenerator classGen, MethodGenerator methodGen, int predicateIndex)240     private void translateStep(ClassGenerator classGen,
241                                MethodGenerator methodGen,
242                                int predicateIndex) {
243         final ConstantPoolGen cpg = classGen.getConstantPool();
244         final InstructionList il = methodGen.getInstructionList();
245 
246         if (predicateIndex >= 0) {
247             translatePredicates(classGen, methodGen, predicateIndex);
248         } else {
249             int star = 0;
250             String name = null;
251             final XSLTC xsltc = getParser().getXSLTC();
252 
253             if (_nodeType >= DTM.NTYPES) {
254                 final Vector ni = xsltc.getNamesIndex();
255 
256                 name = (String)ni.elementAt(_nodeType-DTM.NTYPES);
257                 star = name.lastIndexOf('*');
258             }
259 
260             // If it is an attribute, but not '@*', '@pre:*' or '@node()',
261             // and has no parent
262             if (_axis == Axis.ATTRIBUTE && _nodeType != NodeTest.ATTRIBUTE
263                 && _nodeType != NodeTest.ANODE && !hasParentPattern()
264                 && star == 0)
265             {
266                 int iter = cpg.addInterfaceMethodref(DOM_INTF,
267                                                      "getTypedAxisIterator",
268                                                      "(II)"+NODE_ITERATOR_SIG);
269                 il.append(methodGen.loadDOM());
270                 il.append(new PUSH(cpg, Axis.ATTRIBUTE));
271                 il.append(new PUSH(cpg, _nodeType));
272                 il.append(new INVOKEINTERFACE(iter, 3));
273                 return;
274             }
275 
276             SyntaxTreeNode parent = getParent();
277             // Special case for '.'
278             if (isAbbreviatedDot()) {
279                 if (_type == Type.Node) {
280                     // Put context node on stack if using Type.Node
281                     il.append(methodGen.loadContextNode());
282                 }
283                 else {
284                     if (parent instanceof ParentLocationPath){
285                         // Wrap the context node in a singleton iterator if not.
286                         int init = cpg.addMethodref(SINGLETON_ITERATOR,
287                                                     "<init>",
288                                                     "("+NODE_SIG+")V");
289                         il.append(new NEW(cpg.addClass(SINGLETON_ITERATOR)));
290                         il.append(DUP);
291                         il.append(methodGen.loadContextNode());
292                         il.append(new INVOKESPECIAL(init));
293                     } else {
294                         // DOM.getAxisIterator(int axis);
295                         int git = cpg.addInterfaceMethodref(DOM_INTF,
296                                                 "getAxisIterator",
297                                                 "(I)"+NODE_ITERATOR_SIG);
298                         il.append(methodGen.loadDOM());
299                         il.append(new PUSH(cpg, _axis));
300                         il.append(new INVOKEINTERFACE(git, 2));
301                     }
302                 }
303                 return;
304             }
305 
306             // Special case for /foo/*/bar
307             if ((parent instanceof ParentLocationPath) &&
308                 (parent.getParent() instanceof ParentLocationPath)) {
309                 if ((_nodeType == NodeTest.ELEMENT) && (!_hadPredicates)) {
310                     _nodeType = NodeTest.ANODE;
311                 }
312             }
313 
314             // "ELEMENT" or "*" or "@*" or ".." or "@attr" with a parent.
315             switch (_nodeType) {
316             case NodeTest.ATTRIBUTE:
317                 _axis = Axis.ATTRIBUTE;
318             case NodeTest.ANODE:
319                 // DOM.getAxisIterator(int axis);
320                 int git = cpg.addInterfaceMethodref(DOM_INTF,
321                                                     "getAxisIterator",
322                                                     "(I)"+NODE_ITERATOR_SIG);
323                 il.append(methodGen.loadDOM());
324                 il.append(new PUSH(cpg, _axis));
325                 il.append(new INVOKEINTERFACE(git, 2));
326                 break;
327             default:
328                 if (star > 1) {
329                     final String namespace;
330                     if (_axis == Axis.ATTRIBUTE)
331                         namespace = name.substring(0,star-2);
332                     else
333                         namespace = name.substring(0,star-1);
334 
335                     final int nsType = xsltc.registerNamespace(namespace);
336                     final int ns = cpg.addInterfaceMethodref(DOM_INTF,
337                                                     "getNamespaceAxisIterator",
338                                                     "(II)"+NODE_ITERATOR_SIG);
339                     il.append(methodGen.loadDOM());
340                     il.append(new PUSH(cpg, _axis));
341                     il.append(new PUSH(cpg, nsType));
342                     il.append(new INVOKEINTERFACE(ns, 3));
343                     break;
344                 }
345             case NodeTest.ELEMENT:
346                 // DOM.getTypedAxisIterator(int axis, int type);
347                 final int ty = cpg.addInterfaceMethodref(DOM_INTF,
348                                                 "getTypedAxisIterator",
349                                                 "(II)"+NODE_ITERATOR_SIG);
350                 // Get the typed iterator we're after
351                 il.append(methodGen.loadDOM());
352                 il.append(new PUSH(cpg, _axis));
353                 il.append(new PUSH(cpg, _nodeType));
354                 il.append(new INVOKEINTERFACE(ty, 3));
355 
356                 break;
357             }
358         }
359     }
360 
361 
362     /**
363      * Translate a sequence of predicates. Each predicate is translated
364      * by constructing an instance of <code>CurrentNodeListIterator</code>
365      * which is initialized from another iterator (recursive call),
366      * a filter and a closure (call to translate on the predicate) and "this".
367      */
translatePredicates(ClassGenerator classGen, MethodGenerator methodGen, int predicateIndex)368     public void translatePredicates(ClassGenerator classGen,
369                                     MethodGenerator methodGen,
370                                     int predicateIndex) {
371         final ConstantPoolGen cpg = classGen.getConstantPool();
372         final InstructionList il = methodGen.getInstructionList();
373 
374         int idx = 0;
375 
376         if (predicateIndex < 0) {
377             translateStep(classGen, methodGen, predicateIndex);
378         }
379         else {
380             final Predicate predicate = (Predicate) _predicates.get(predicateIndex--);
381 
382             // Special case for predicates that can use the NodeValueIterator
383             // instead of an auxiliary class. Certain path/predicates pairs
384             // are translated into a base path, on top of which we place a
385             // node value iterator that tests for the desired value:
386             //   foo[@attr = 'str']  ->  foo/@attr + test(value='str')
387             //   foo[bar = 'str']    ->  foo/bar + test(value='str')
388             //   foo/bar[. = 'str']  ->  foo/bar + test(value='str')
389             if (predicate.isNodeValueTest()) {
390                 Step step = predicate.getStep();
391 
392                 il.append(methodGen.loadDOM());
393                 // If the predicate's Step is simply '.' we translate this Step
394                 // and place the node test on top of the resulting iterator
395                 if (step.isAbbreviatedDot()) {
396                     translateStep(classGen, methodGen, predicateIndex);
397                     il.append(new ICONST(DOM.RETURN_CURRENT));
398                 }
399                 // Otherwise we create a parent location path with this Step and
400                 // the predicates Step, and place the node test on top of that
401                 else {
402                     ParentLocationPath path = new ParentLocationPath(this, step);
403                     _parent = step._parent = path;      // Force re-parenting
404 
405                     try {
406                         path.typeCheck(getParser().getSymbolTable());
407                     }
408                     catch (TypeCheckError e) { }
409                     translateStep(classGen, methodGen, predicateIndex);
410                     path.translateStep(classGen, methodGen);
411                     il.append(new ICONST(DOM.RETURN_PARENT));
412                 }
413                 predicate.translate(classGen, methodGen);
414                 idx = cpg.addInterfaceMethodref(DOM_INTF,
415                                                 GET_NODE_VALUE_ITERATOR,
416                                                 GET_NODE_VALUE_ITERATOR_SIG);
417                 il.append(new INVOKEINTERFACE(idx, 5));
418             }
419             // Handle '//*[n]' expression
420             else if (predicate.isNthDescendant()) {
421                 il.append(methodGen.loadDOM());
422                 // il.append(new ICONST(NodeTest.ELEMENT));
423                 il.append(new PUSH(cpg, predicate.getPosType()));
424                 predicate.translate(classGen, methodGen);
425                 il.append(new ICONST(0));
426                 idx = cpg.addInterfaceMethodref(DOM_INTF,
427                                                 "getNthDescendant",
428                                                 "(IIZ)"+NODE_ITERATOR_SIG);
429                 il.append(new INVOKEINTERFACE(idx, 4));
430             }
431             // Handle 'elem[n]' expression
432             else if (predicate.isNthPositionFilter()) {
433                 idx = cpg.addMethodref(NTH_ITERATOR_CLASS,
434                                        "<init>",
435                                        "("+NODE_ITERATOR_SIG+"I)V");
436 
437                 // Backwards branches are prohibited if an uninitialized object
438                 // is on the stack by section 4.9.4 of the JVM Specification,
439                 // 2nd Ed.  We don't know whether this code might contain
440                 // backwards branches, so we mustn't create the new object until
441                 // after we've created the suspect arguments to its constructor.
442                 // Instead we calculate the values of the arguments to the
443                 // constructor first, store them in temporary variables, create
444                 // the object and reload the arguments from the temporaries to
445                 // avoid the problem.
446                 translatePredicates(classGen, methodGen, predicateIndex); // recursive call
447                 LocalVariableGen iteratorTemp
448                         = methodGen.addLocalVariable("step_tmp1",
449                                          Util.getJCRefType(NODE_ITERATOR_SIG),
450                                          null, null);
451                 iteratorTemp.setStart(
452                         il.append(new ASTORE(iteratorTemp.getIndex())));
453 
454                 predicate.translate(classGen, methodGen);
455                 LocalVariableGen predicateValueTemp
456                         = methodGen.addLocalVariable("step_tmp2",
457                                          Util.getJCRefType("I"),
458                                          null, null);
459                 predicateValueTemp.setStart(
460                         il.append(new ISTORE(predicateValueTemp.getIndex())));
461 
462                 il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS)));
463                 il.append(DUP);
464                 iteratorTemp.setEnd(
465                         il.append(new ALOAD(iteratorTemp.getIndex())));
466                 predicateValueTemp.setEnd(
467                         il.append(new ILOAD(predicateValueTemp.getIndex())));
468                 il.append(new INVOKESPECIAL(idx));
469             }
470             else {
471                 idx = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
472                                        "<init>",
473                                        "("
474                                        + NODE_ITERATOR_SIG
475                                        + CURRENT_NODE_LIST_FILTER_SIG
476                                        + NODE_SIG
477                                        + TRANSLET_SIG
478                                        + ")V");
479 
480                 // Backwards branches are prohibited if an uninitialized object
481                 // is on the stack by section 4.9.4 of the JVM Specification,
482                 // 2nd Ed.  We don't know whether this code might contain
483                 // backwards branches, so we mustn't create the new object until
484                 // after we've created the suspect arguments to its constructor.
485                 // Instead we calculate the values of the arguments to the
486                 // constructor first, store them in temporary variables, create
487                 // the object and reload the arguments from the temporaries to
488                 // avoid the problem.
489                 translatePredicates(classGen, methodGen, predicateIndex); // recursive call
490                 LocalVariableGen iteratorTemp
491                         = methodGen.addLocalVariable("step_tmp1",
492                                          Util.getJCRefType(NODE_ITERATOR_SIG),
493                                          null, null);
494                 iteratorTemp.setStart(
495                         il.append(new ASTORE(iteratorTemp.getIndex())));
496 
497                 predicate.translateFilter(classGen, methodGen);
498                 LocalVariableGen filterTemp
499                         = methodGen.addLocalVariable("step_tmp2",
500                               Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG),
501                               null, null);
502                 filterTemp.setStart(
503                         il.append(new ASTORE(filterTemp.getIndex())));
504                 // create new CurrentNodeListIterator
505                 il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR)));
506                 il.append(DUP);
507 
508                 iteratorTemp.setEnd(
509                         il.append(new ALOAD(iteratorTemp.getIndex())));
510                 filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex())));
511 
512                 il.append(methodGen.loadCurrentNode());
513                 il.append(classGen.loadTranslet());
514                 if (classGen.isExternal()) {
515                     final String className = classGen.getClassName();
516                     il.append(new CHECKCAST(cpg.addClass(className)));
517                 }
518                 il.append(new INVOKESPECIAL(idx));
519             }
520         }
521     }
522 
523     /**
524      * Returns a string representation of this step.
525      */
toString()526     public String toString() {
527         final StringBuffer buffer = new StringBuffer("step(\"");
528         buffer.append(Axis.getNames(_axis)).append("\", ").append(_nodeType);
529         if (_predicates != null) {
530             final int n = _predicates.size();
531             for (int i = 0; i < n; i++) {
532                 final Predicate pred = (Predicate)_predicates.elementAt(i);
533                 buffer.append(", ").append(pred.toString());
534             }
535         }
536         return buffer.append(')').toString();
537     }
538 }
539