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 }