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