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