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.classfile.Field;
  24 import com.sun.org.apache.bcel.internal.generic.ALOAD;
  25 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  26 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  27 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  28 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  29 import com.sun.org.apache.bcel.internal.generic.GOTO;
  30 import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  31 import com.sun.org.apache.bcel.internal.generic.IFLT;
  32 import com.sun.org.apache.bcel.internal.generic.IFNE;
  33 import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
  34 import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ;
  35 import com.sun.org.apache.bcel.internal.generic.IF_ICMPLT;
  36 import com.sun.org.apache.bcel.internal.generic.IF_ICMPNE;
  37 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  38 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  39 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  40 import com.sun.org.apache.bcel.internal.generic.ISTORE;
  41 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  42 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  43 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  44 import com.sun.org.apache.bcel.internal.generic.NEW;
  45 import com.sun.org.apache.bcel.internal.generic.PUSH;
  46 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  51 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  52 import com.sun.org.apache.xml.internal.dtm.Axis;
  53 import com.sun.org.apache.xml.internal.dtm.DTM;
  54 import java.util.List;
  55 
  56 /**
  57  * @author Jacek Ambroziak
  58  * @author Santiago Pericas-Geertsen
  59  * @author Erwin Bolwidt <ejb@klomp.org>
  60  * @LastModified: Oct 2017
  61  */
  62 class StepPattern extends RelativePathPattern {
  63 
  64     private static final int NO_CONTEXT = 0;
  65     private static final int SIMPLE_CONTEXT = 1;
  66     private static final int GENERAL_CONTEXT = 2;
  67 
  68     protected final int _axis;
  69     protected final int _nodeType;
  70     protected List<Predicate> _predicates;
  71 
  72     private Step    _step = null;
  73     private boolean _isEpsilon = false;
  74     private int     _contextCase;
  75 
  76     private double  _priority = Double.MAX_VALUE;
  77 
  78     public StepPattern(int axis, int nodeType, List<Predicate> predicates) {
  79         _axis = axis;
  80         _nodeType = nodeType;
  81         _predicates = predicates;
  82     }
  83 
  84     public void setParser(Parser parser) {
  85         super.setParser(parser);
  86         if (_predicates != null) {
  87             for (Predicate exp : _predicates) {
  88                 exp.setParser(parser);
  89                 exp.setParent(this);
  90             }
  91         }
  92     }
  93 
  94     public int getNodeType() {
  95         return _nodeType;
  96     }
  97 
  98     public void setPriority(double priority) {
  99         _priority = priority;
 100     }
 101 
 102     public StepPattern getKernelPattern() {
 103         return this;
 104     }
 105 
 106     public boolean isWildcard() {
 107         return _isEpsilon && hasPredicates() == false;
 108     }
 109 
 110     public StepPattern setPredicates(List<Predicate> predicates) {
 111         _predicates = predicates;
 112         return(this);
 113     }
 114 
 115     protected boolean hasPredicates() {
 116         return _predicates != null && _predicates.size() > 0;
 117     }
 118 
 119     public double getDefaultPriority() {
 120         if (_priority != Double.MAX_VALUE) {
 121             return _priority;
 122         }
 123 
 124         if (hasPredicates()) {
 125             return 0.5;
 126         }
 127         else {
 128             switch(_nodeType) {
 129             case -1:
 130                 return -0.5;    // node()
 131             case 0:
 132                 return 0.0;
 133             default:
 134                 return (_nodeType >= NodeTest.GTYPE) ? 0.0 : -0.5;
 135             }
 136         }
 137     }
 138 
 139     public int getAxis() {
 140         return _axis;
 141     }
 142 
 143     public void reduceKernelPattern() {
 144         _isEpsilon = true;
 145     }
 146 
 147     public String toString() {
 148         final StringBuffer buffer = new StringBuffer("stepPattern(\"");
 149         buffer.append(Axis.getNames(_axis))
 150             .append("\", ")
 151             .append(_isEpsilon ?
 152                         ("epsilon{" + Integer.toString(_nodeType) + "}") :
 153                          Integer.toString(_nodeType));
 154         if (_predicates != null)
 155             buffer.append(", ").append(_predicates.toString());
 156         return buffer.append(')').toString();
 157     }
 158 
 159     private int analyzeCases() {
 160         boolean noContext = true;
 161         final int n = _predicates.size();
 162 
 163         for (int i = 0; i < n && noContext; i++) {
 164             Predicate pred = _predicates.get(i);
 165             if (pred.isNthPositionFilter() ||
 166                 pred.hasPositionCall() ||
 167                 pred.hasLastCall())
 168             {
 169                 noContext = false;
 170             }
 171         }
 172 
 173         if (noContext) {
 174             return NO_CONTEXT;
 175         }
 176         else if (n == 1) {
 177             return SIMPLE_CONTEXT;
 178         }
 179         return GENERAL_CONTEXT;
 180     }
 181 
 182     private String getNextFieldName() {
 183         return  "__step_pattern_iter_" + getXSLTC().nextStepPatternSerial();
 184     }
 185 
 186     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 187         if (hasPredicates()) {
 188             // Type check all the predicates (e -> position() = e)
 189             for (Predicate pred : _predicates) {
 190                 pred.typeCheck(stable);
 191             }
 192 
 193             // Analyze context cases
 194             _contextCase = analyzeCases();
 195 
 196             Step step = null;
 197 
 198             // Create an instance of Step to do the translation
 199             if (_contextCase == SIMPLE_CONTEXT) {
 200                 Predicate pred = _predicates.get(0);
 201                 if (pred.isNthPositionFilter()) {
 202                     _contextCase = GENERAL_CONTEXT;
 203                     step = new Step(_axis, _nodeType, _predicates);
 204                 } else {
 205                     step = new Step(_axis, _nodeType, null);
 206                 }
 207             } else if (_contextCase == GENERAL_CONTEXT) {
 208                 for (Predicate pred : _predicates) {
 209                     pred.dontOptimize();
 210                 }
 211 
 212                 step = new Step(_axis, _nodeType, _predicates);
 213             }
 214 
 215             if (step != null) {
 216                 step.setParser(getParser());
 217                 step.typeCheck(stable);
 218                 _step = step;
 219             }
 220         }
 221         return _axis == Axis.CHILD ? Type.Element : Type.Attribute;
 222     }
 223 
 224     private void translateKernel(ClassGenerator classGen,
 225                                  MethodGenerator methodGen) {
 226         final ConstantPoolGen cpg = classGen.getConstantPool();
 227         final InstructionList il = methodGen.getInstructionList();
 228 
 229         if (_nodeType == DTM.ELEMENT_NODE) {
 230             final int check = cpg.addInterfaceMethodref(DOM_INTF,
 231                                                         "isElement", "(I)Z");
 232             il.append(methodGen.loadDOM());
 233             il.append(SWAP);
 234             il.append(new INVOKEINTERFACE(check, 2));
 235 
 236             // Need to allow for long jumps here
 237             final BranchHandle icmp = il.append(new IFNE(null));
 238             _falseList.add(il.append(new GOTO_W(null)));
 239             icmp.setTarget(il.append(NOP));
 240         }
 241         else if (_nodeType == DTM.ATTRIBUTE_NODE) {
 242             final int check = cpg.addInterfaceMethodref(DOM_INTF,
 243                                                         "isAttribute", "(I)Z");
 244             il.append(methodGen.loadDOM());
 245             il.append(SWAP);
 246             il.append(new INVOKEINTERFACE(check, 2));
 247 
 248             // Need to allow for long jumps here
 249             final BranchHandle icmp = il.append(new IFNE(null));
 250             _falseList.add(il.append(new GOTO_W(null)));
 251             icmp.setTarget(il.append(NOP));
 252         }
 253         else {
 254             // context node is on the stack
 255             final int getEType = cpg.addInterfaceMethodref(DOM_INTF,
 256                                                           "getExpandedTypeID",
 257                                                           "(I)I");
 258             il.append(methodGen.loadDOM());
 259             il.append(SWAP);
 260             il.append(new INVOKEINTERFACE(getEType, 2));
 261             il.append(new PUSH(cpg, _nodeType));
 262 
 263             // Need to allow for long jumps here
 264             final BranchHandle icmp = il.append(new IF_ICMPEQ(null));
 265             _falseList.add(il.append(new GOTO_W(null)));
 266             icmp.setTarget(il.append(NOP));
 267         }
 268     }
 269 
 270     private void translateNoContext(ClassGenerator classGen,
 271                                     MethodGenerator methodGen) {
 272         final ConstantPoolGen cpg = classGen.getConstantPool();
 273         final InstructionList il = methodGen.getInstructionList();
 274 
 275         // Push current node on the stack
 276         il.append(methodGen.loadCurrentNode());
 277         il.append(SWAP);
 278 
 279         // Overwrite current node with matching node
 280         il.append(methodGen.storeCurrentNode());
 281 
 282         // If pattern not reduced then check kernel
 283         if (!_isEpsilon) {
 284             il.append(methodGen.loadCurrentNode());
 285             translateKernel(classGen, methodGen);
 286         }
 287 
 288         // Compile the expressions within the predicates
 289         for (Predicate pred : _predicates) {
 290             Expression exp = pred.getExpr();
 291             exp.translateDesynthesized(classGen, methodGen);
 292             _trueList.append(exp._trueList);
 293             _falseList.append(exp._falseList);
 294         }
 295 
 296         // Backpatch true list and restore current iterator/node
 297         InstructionHandle restore;
 298         restore = il.append(methodGen.storeCurrentNode());
 299         backPatchTrueList(restore);
 300         BranchHandle skipFalse = il.append(new GOTO(null));
 301 
 302         // Backpatch false list and restore current iterator/node
 303         restore = il.append(methodGen.storeCurrentNode());
 304         backPatchFalseList(restore);
 305         _falseList.add(il.append(new GOTO(null)));
 306 
 307         // True list falls through
 308         skipFalse.setTarget(il.append(NOP));
 309     }
 310 
 311     private void translateSimpleContext(ClassGenerator classGen,
 312                                         MethodGenerator methodGen) {
 313         int index;
 314         final ConstantPoolGen cpg = classGen.getConstantPool();
 315         final InstructionList il = methodGen.getInstructionList();
 316 
 317         // Store matching node into a local variable
 318         LocalVariableGen match;
 319         match = methodGen.addLocalVariable("step_pattern_tmp1",
 320                                            Util.getJCRefType(NODE_SIG),
 321                                            null, null);
 322         match.setStart(il.append(new ISTORE(match.getIndex())));
 323 
 324         // If pattern not reduced then check kernel
 325         if (!_isEpsilon) {
 326             il.append(new ILOAD(match.getIndex()));
 327             translateKernel(classGen, methodGen);
 328         }
 329 
 330         // Push current iterator and current node on the stack
 331         il.append(methodGen.loadCurrentNode());
 332         il.append(methodGen.loadIterator());
 333 
 334         // Create a new matching iterator using the matching node
 335         index = cpg.addMethodref(MATCHING_ITERATOR, "<init>",
 336                                  "(I" + NODE_ITERATOR_SIG + ")V");
 337 
 338         // Backwards branches are prohibited if an uninitialized object is
 339         // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
 340         // We don't know whether this code might contain backwards branches,
 341         // so we mustn't create the new object until after we've created
 342         // the suspect arguments to its constructor.  Instead we calculate
 343         // the values of the arguments to the constructor first, store them
 344         // in temporary variables, create the object and reload the
 345         // arguments from the temporaries to avoid the problem.
 346 
 347         _step.translate(classGen, methodGen);
 348         LocalVariableGen stepIteratorTemp =
 349                 methodGen.addLocalVariable("step_pattern_tmp2",
 350                                            Util.getJCRefType(NODE_ITERATOR_SIG),
 351                                            null, null);
 352         stepIteratorTemp.setStart(
 353                 il.append(new ASTORE(stepIteratorTemp.getIndex())));
 354 
 355         il.append(new NEW(cpg.addClass(MATCHING_ITERATOR)));
 356         il.append(DUP);
 357         il.append(new ILOAD(match.getIndex()));
 358         stepIteratorTemp.setEnd(
 359                 il.append(new ALOAD(stepIteratorTemp.getIndex())));
 360         il.append(new INVOKESPECIAL(index));
 361 
 362         // Get the parent of the matching node
 363         il.append(methodGen.loadDOM());
 364         il.append(new ILOAD(match.getIndex()));
 365         index = cpg.addInterfaceMethodref(DOM_INTF, GET_PARENT, GET_PARENT_SIG);
 366         il.append(new INVOKEINTERFACE(index, 2));
 367 
 368         // Start the iterator with the parent
 369         il.append(methodGen.setStartNode());
 370 
 371         // Overwrite current iterator and current node
 372         il.append(methodGen.storeIterator());
 373         match.setEnd(il.append(new ILOAD(match.getIndex())));
 374         il.append(methodGen.storeCurrentNode());
 375 
 376         // Translate the expression of the predicate
 377         Predicate pred = _predicates.get(0);
 378         Expression exp = pred.getExpr();
 379         exp.translateDesynthesized(classGen, methodGen);
 380 
 381         // Backpatch true list and restore current iterator/node
 382         InstructionHandle restore = il.append(methodGen.storeIterator());
 383         il.append(methodGen.storeCurrentNode());
 384         exp.backPatchTrueList(restore);
 385         BranchHandle skipFalse = il.append(new GOTO(null));
 386 
 387         // Backpatch false list and restore current iterator/node
 388         restore = il.append(methodGen.storeIterator());
 389         il.append(methodGen.storeCurrentNode());
 390         exp.backPatchFalseList(restore);
 391         _falseList.add(il.append(new GOTO(null)));
 392 
 393         // True list falls through
 394         skipFalse.setTarget(il.append(NOP));
 395     }
 396 
 397     private void translateGeneralContext(ClassGenerator classGen,
 398                                          MethodGenerator methodGen) {
 399         final ConstantPoolGen cpg = classGen.getConstantPool();
 400         final InstructionList il = methodGen.getInstructionList();
 401 
 402         int iteratorIndex = 0;
 403         BranchHandle ifBlock = null;
 404         LocalVariableGen iter, node, node2;
 405         final String iteratorName = getNextFieldName();
 406 
 407         // Store node on the stack into a local variable
 408         node = methodGen.addLocalVariable("step_pattern_tmp1",
 409                                           Util.getJCRefType(NODE_SIG),
 410                                           null, null);
 411         node.setStart(il.append(new ISTORE(node.getIndex())));
 412 
 413         // Create a new local to store the iterator
 414         iter = methodGen.addLocalVariable("step_pattern_tmp2",
 415                                           Util.getJCRefType(NODE_ITERATOR_SIG),
 416                                           null, null);
 417 
 418         // Add a new private field if this is the main class
 419         if (!classGen.isExternal()) {
 420             final Field iterator =
 421                 new Field(ACC_PRIVATE,
 422                           cpg.addUtf8(iteratorName),
 423                           cpg.addUtf8(NODE_ITERATOR_SIG),
 424                           null, cpg.getConstantPool());
 425             classGen.addField(iterator);
 426             iteratorIndex = cpg.addFieldref(classGen.getClassName(),
 427                                             iteratorName,
 428                                             NODE_ITERATOR_SIG);
 429 
 430             il.append(classGen.loadTranslet());
 431             il.append(new GETFIELD(iteratorIndex));
 432             il.append(DUP);
 433             iter.setStart(il.append(new ASTORE(iter.getIndex())));
 434             ifBlock = il.append(new IFNONNULL(null));
 435             il.append(classGen.loadTranslet());
 436         }
 437 
 438         // Compile the step created at type checking time
 439         _step.translate(classGen, methodGen);
 440         InstructionHandle iterStore = il.append(new ASTORE(iter.getIndex()));
 441 
 442         // If in the main class update the field too
 443         if (!classGen.isExternal()) {
 444             il.append(new ALOAD(iter.getIndex()));
 445             il.append(new PUTFIELD(iteratorIndex));
 446             ifBlock.setTarget(il.append(NOP));
 447         } else {
 448             // If class is not external, start of range for iter variable was
 449             // set above
 450             iter.setStart(iterStore);
 451         }
 452 
 453         // Get the parent of the node on the stack
 454         il.append(methodGen.loadDOM());
 455         il.append(new ILOAD(node.getIndex()));
 456         int index = cpg.addInterfaceMethodref(DOM_INTF,
 457                                               GET_PARENT, GET_PARENT_SIG);
 458         il.append(new INVOKEINTERFACE(index, 2));
 459 
 460         // Initialize the iterator with the parent
 461         il.append(new ALOAD(iter.getIndex()));
 462         il.append(SWAP);
 463         il.append(methodGen.setStartNode());
 464 
 465         /*
 466          * Inline loop:
 467          *
 468          * int node2;
 469          * while ((node2 = iter.next()) != NodeIterator.END
 470          *                && node2 < node);
 471          * return node2 == node;
 472          */
 473         BranchHandle skipNext;
 474         InstructionHandle begin, next;
 475         node2 = methodGen.addLocalVariable("step_pattern_tmp3",
 476                                            Util.getJCRefType(NODE_SIG),
 477                                            null, null);
 478 
 479         skipNext = il.append(new GOTO(null));
 480         next = il.append(new ALOAD(iter.getIndex()));
 481         node2.setStart(next);
 482         begin = il.append(methodGen.nextNode());
 483         il.append(DUP);
 484         il.append(new ISTORE(node2.getIndex()));
 485         _falseList.add(il.append(new IFLT(null)));      // NodeIterator.END
 486 
 487         il.append(new ILOAD(node2.getIndex()));
 488         il.append(new ILOAD(node.getIndex()));
 489         iter.setEnd(il.append(new IF_ICMPLT(next)));
 490 
 491         node2.setEnd(il.append(new ILOAD(node2.getIndex())));
 492         node.setEnd(il.append(new ILOAD(node.getIndex())));
 493         _falseList.add(il.append(new IF_ICMPNE(null)));
 494 
 495         skipNext.setTarget(begin);
 496     }
 497 
 498     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 499         final ConstantPoolGen cpg = classGen.getConstantPool();
 500         final InstructionList il = methodGen.getInstructionList();
 501 
 502         if (hasPredicates()) {
 503             switch (_contextCase) {
 504             case NO_CONTEXT:
 505                 translateNoContext(classGen, methodGen);
 506                 break;
 507 
 508             case SIMPLE_CONTEXT:
 509                 translateSimpleContext(classGen, methodGen);
 510                 break;
 511 
 512             default:
 513                 translateGeneralContext(classGen, methodGen);
 514                 break;
 515             }
 516         }
 517         else if (isWildcard()) {
 518             il.append(POP);     // true list falls through
 519         }
 520         else {
 521             translateKernel(classGen, methodGen);
 522         }
 523     }
 524 }