1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  23 
  24 import com.sun.org.apache.bcel.internal.classfile.Field;
  25 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  26 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  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.INVOKESPECIAL;
  30 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  31 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  32 import com.sun.org.apache.bcel.internal.generic.NEW;
  33 import com.sun.org.apache.bcel.internal.generic.PUSH;
  34 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.FilterGenerator;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
  41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TestGenerator;
  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.xalan.internal.xsltc.runtime.Operators;
  48 import java.util.ArrayList;
  49 import java.util.List;
  50 
  51 /**
  52  * @author Jacek Ambroziak
  53  * @author Santiago Pericas-Geertsen
  54  * @author Morten Jorgensen
  55  */
  56 final class Predicate extends Expression implements Closure {
  57 
  58     /**
  59      * The predicate's expression.
  60      */
  61     private Expression _exp = null;
  62 
  63     /**
  64      * This flag indicates if optimizations are turned on. The
  65      * method <code>dontOptimize()</code> can be called to turn
  66      * optimizations off.
  67      */
  68     private boolean _canOptimize = true;
  69 
  70     /**
  71      * Flag indicatig if the nth position optimization is on. It
  72      * is set in <code>typeCheck()</code>.
  73      */
  74     private boolean _nthPositionFilter = false;
  75 
  76     /**
  77      * Flag indicatig if the nth position descendant is on. It
  78      * is set in <code>typeCheck()</code>.
  79      */
  80     private boolean _nthDescendant = false;
  81 
  82     /**
  83      * Cached node type of the expression that owns this predicate.
  84      */
  85     int _ptype = -1;
  86 
  87     /**
  88      * Name of the inner class.
  89      */
  90     private String _className = null;
  91 
  92     /**
  93      * List of variables in closure.
  94      */
  95     private List<VariableRefBase> _closureVars = null;
  96 
  97     /**
  98      * Reference to parent closure.
  99      */
 100     private Closure _parentClosure = null;
 101 
 102     /**
 103      * Cached value of method <code>getCompareValue()</code>.
 104      */
 105     private Expression _value = null;
 106 
 107     /**
 108      * Cached value of method <code>getCompareValue()</code>.
 109      */
 110     private Step _step = null;
 111 
 112     /**
 113      * Initializes a predicate.
 114      */
 115     public Predicate(Expression exp) {
 116         _exp = exp;
 117         _exp.setParent(this);
 118 
 119     }
 120 
 121     /**
 122      * Set the parser for this expression.
 123      */
 124     public void setParser(Parser parser) {
 125         super.setParser(parser);
 126         _exp.setParser(parser);
 127     }
 128 
 129     /**
 130      * Returns a boolean value indicating if the nth position optimization
 131      * is on. Must be call after type checking!
 132      */
 133     public boolean isNthPositionFilter() {
 134         return _nthPositionFilter;
 135     }
 136 
 137     /**
 138      * Returns a boolean value indicating if the nth descendant optimization
 139      * is on. Must be call after type checking!
 140      */
 141     public boolean isNthDescendant() {
 142         return _nthDescendant;
 143     }
 144 
 145     /**
 146      * Turns off all optimizations for this predicate.
 147      */
 148     public void dontOptimize() {
 149         _canOptimize = false;
 150     }
 151 
 152     /**
 153      * Returns true if the expression in this predicate contains a call
 154      * to position().
 155      */
 156     public boolean hasPositionCall() {
 157         return _exp.hasPositionCall();
 158     }
 159 
 160     /**
 161      * Returns true if the expression in this predicate contains a call
 162      * to last().
 163      */
 164     public boolean hasLastCall() {
 165         return _exp.hasLastCall();
 166     }
 167 
 168     // -- Begin Closure interface --------------------
 169 
 170     /**
 171      * Returns true if this closure is compiled in an inner class (i.e.
 172      * if this is a real closure).
 173      */
 174     public boolean inInnerClass() {
 175         return (_className != null);
 176     }
 177 
 178     /**
 179      * Returns a reference to its parent closure or null if outermost.
 180      */
 181     public Closure getParentClosure() {
 182         if (_parentClosure == null) {
 183             SyntaxTreeNode node = getParent();
 184             do {
 185                 if (node instanceof Closure) {
 186                     _parentClosure = (Closure) node;
 187                     break;
 188                 }
 189                 if (node instanceof TopLevelElement) {
 190                     break;      // way up in the tree
 191                 }
 192                 node = node.getParent();
 193             } while (node != null);
 194         }
 195         return _parentClosure;
 196     }
 197 
 198     /**
 199      * Returns the name of the auxiliary class or null if this predicate
 200      * is compiled inside the Translet.
 201      */
 202     public String getInnerClassName() {
 203         return _className;
 204     }
 205 
 206     /**
 207      * Add new variable to the closure.
 208      */
 209     public void addVariable(VariableRefBase variableRef) {
 210         if (_closureVars == null) {
 211             _closureVars = new ArrayList<>();
 212         }
 213 
 214         // Only one reference per variable
 215         if (!_closureVars.contains(variableRef)) {
 216             _closureVars.add(variableRef);
 217 
 218             // Add variable to parent closure as well
 219             Closure parentClosure = getParentClosure();
 220             if (parentClosure != null) {
 221                 parentClosure.addVariable(variableRef);
 222             }
 223         }
 224     }
 225 
 226     // -- End Closure interface ----------------------
 227 
 228     /**
 229      * Returns the node type of the expression owning this predicate. The
 230      * return value is cached in <code>_ptype</code>.
 231      */
 232     public int getPosType() {
 233         if (_ptype == -1) {
 234             SyntaxTreeNode parent = getParent();
 235             if (parent instanceof StepPattern) {
 236                 _ptype = ((StepPattern)parent).getNodeType();
 237             }
 238             else if (parent instanceof AbsoluteLocationPath) {
 239                 AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
 240                 Expression exp = path.getPath();
 241                 if (exp instanceof Step) {
 242                     _ptype = ((Step)exp).getNodeType();
 243                 }
 244             }
 245             else if (parent instanceof VariableRefBase) {
 246                 final VariableRefBase ref = (VariableRefBase)parent;
 247                 final VariableBase var = ref.getVariable();
 248                 final Expression exp = var.getExpression();
 249                 if (exp instanceof Step) {
 250                     _ptype = ((Step)exp).getNodeType();
 251                 }
 252             }
 253             else if (parent instanceof Step) {
 254                 _ptype = ((Step)parent).getNodeType();
 255             }
 256         }
 257         return _ptype;
 258     }
 259 
 260     public boolean parentIsPattern() {
 261         return (getParent() instanceof Pattern);
 262     }
 263 
 264     public Expression getExpr() {
 265         return _exp;
 266     }
 267 
 268     public String toString() {
 269         return "pred(" + _exp + ')';
 270     }
 271 
 272     /**
 273      * Type check a predicate expression. If the type of the expression is
 274      * number convert it to boolean by adding a comparison with position().
 275      * Note that if the expression is a parameter, we cannot distinguish
 276      * at compile time if its type is number or not. Hence, expressions of
 277      * reference type are always converted to booleans.
 278      *
 279      * This method may be called twice, before and after calling
 280      * <code>dontOptimize()</code>. If so, the second time it should honor
 281      * the new value of <code>_canOptimize</code>.
 282      */
 283     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 284         Type texp = _exp.typeCheck(stable);
 285 
 286         // We need explicit type information for reference types - no good!
 287         if (texp instanceof ReferenceType) {
 288             _exp = new CastExpr(_exp, texp = Type.Real);
 289         }
 290 
 291         // A result tree fragment should not be cast directly to a number type,
 292         // but rather to a boolean value, and then to a numer (0 or 1).
 293         // Ref. section 11.2 of the XSLT 1.0 spec
 294         if (texp instanceof ResultTreeType) {
 295             _exp = new CastExpr(_exp, Type.Boolean);
 296             _exp = new CastExpr(_exp, Type.Real);
 297             texp = _exp.typeCheck(stable);
 298         }
 299 
 300         // Numerical types will be converted to a position filter
 301         if (texp instanceof NumberType) {
 302             // Cast any numerical types to an integer
 303             if (texp instanceof IntType == false) {
 304                 _exp = new CastExpr(_exp, Type.Int);
 305             }
 306 
 307             if (_canOptimize) {
 308                 // Nth position optimization. Expression must not depend on context
 309                 _nthPositionFilter =
 310                     !_exp.hasLastCall() && !_exp.hasPositionCall();
 311 
 312                 // _nthDescendant optimization - only if _nthPositionFilter is on
 313                 if (_nthPositionFilter) {
 314                     SyntaxTreeNode parent = getParent();
 315                     _nthDescendant = (parent instanceof Step) &&
 316                         (parent.getParent() instanceof AbsoluteLocationPath);
 317                     return _type = Type.NodeSet;
 318                 }
 319             }
 320 
 321            // Reset optimization flags
 322             _nthPositionFilter = _nthDescendant = false;
 323 
 324            // Otherwise, expand [e] to [position() = e]
 325            final QName position =
 326                 getParser().getQNameIgnoreDefaultNs("position");
 327            final PositionCall positionCall =
 328                 new PositionCall(position);
 329            positionCall.setParser(getParser());
 330            positionCall.setParent(this);
 331 
 332            _exp = new EqualityExpr(Operators.EQ, positionCall,
 333                                     _exp);
 334            if (_exp.typeCheck(stable) != Type.Boolean) {
 335                _exp = new CastExpr(_exp, Type.Boolean);
 336            }
 337            return _type = Type.Boolean;
 338         }
 339         else {
 340             // All other types will be handled as boolean values
 341             if (texp instanceof BooleanType == false) {
 342                 _exp = new CastExpr(_exp, Type.Boolean);
 343             }
 344             return _type = Type.Boolean;
 345         }
 346     }
 347 
 348     /**
 349      * Create a new "Filter" class implementing
 350      * <code>CurrentNodeListFilter</code>. Allocate registers for local
 351      * variables and local parameters passed in the closure to test().
 352      * Notice that local variables need to be "unboxed".
 353      */
 354     private void compileFilter(ClassGenerator classGen,
 355                                MethodGenerator methodGen) {
 356         TestGenerator testGen;
 357         LocalVariableGen local;
 358         FilterGenerator filterGen;
 359 
 360         _className = getXSLTC().getHelperClassName();
 361         filterGen = new FilterGenerator(_className,
 362                                         "java.lang.Object",
 363                                         toString(),
 364                                         ACC_PUBLIC | ACC_SUPER,
 365                                         new String[] {
 366                                             CURRENT_NODE_LIST_FILTER
 367                                         },
 368                                         classGen.getStylesheet());
 369 
 370         final ConstantPoolGen cpg = filterGen.getConstantPool();
 371         final int length = (_closureVars == null) ? 0 : _closureVars.size();
 372 
 373         // Add a new instance variable for each var in closure
 374         for (int i = 0; i < length; i++) {
 375             VariableBase var = (_closureVars.get(i)).getVariable();
 376 
 377             filterGen.addField(new Field(ACC_PUBLIC,
 378                                         cpg.addUtf8(var.getEscapedName()),
 379                                         cpg.addUtf8(var.getType().toSignature()),
 380                                         null, cpg.getConstantPool()));
 381         }
 382 
 383         final InstructionList il = new InstructionList();
 384         testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
 385                                     com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
 386                                     new com.sun.org.apache.bcel.internal.generic.Type[] {
 387                                         com.sun.org.apache.bcel.internal.generic.Type.INT,
 388                                         com.sun.org.apache.bcel.internal.generic.Type.INT,
 389                                         com.sun.org.apache.bcel.internal.generic.Type.INT,
 390                                         com.sun.org.apache.bcel.internal.generic.Type.INT,
 391                                         Util.getJCRefType(TRANSLET_SIG),
 392                                         Util.getJCRefType(NODE_ITERATOR_SIG)
 393                                     },
 394                                     new String[] {
 395                                         "node",
 396                                         "position",
 397                                         "last",
 398                                         "current",
 399                                         "translet",
 400                                         "iterator"
 401                                     },
 402                                     "test", _className, il, cpg);
 403 
 404         // Store the dom in a local variable
 405         local = testGen.addLocalVariable("document",
 406                                          Util.getJCRefType(DOM_INTF_SIG),
 407                                          null, null);
 408         final String className = classGen.getClassName();
 409         il.append(filterGen.loadTranslet());
 410         il.append(new CHECKCAST(cpg.addClass(className)));
 411         il.append(new GETFIELD(cpg.addFieldref(className,
 412                                                DOM_FIELD, DOM_INTF_SIG)));
 413         local.setStart(il.append(new ASTORE(local.getIndex())));
 414 
 415         // Store the dom index in the test generator
 416         testGen.setDomIndex(local.getIndex());
 417 
 418         _exp.translate(filterGen, testGen);
 419         il.append(IRETURN);
 420 
 421         filterGen.addEmptyConstructor(ACC_PUBLIC);
 422         filterGen.addMethod(testGen);
 423 
 424         getXSLTC().dumpClass(filterGen.getJavaClass());
 425     }
 426 
 427     /**
 428      * Returns true if the predicate is a test for the existance of an
 429      * element or attribute. All we have to do is to get the first node
 430      * from the step, check if it is there, and then return true/false.
 431      */
 432     public boolean isBooleanTest() {
 433         return (_exp instanceof BooleanExpr);
 434     }
 435 
 436     /**
 437      * Method to see if we can optimise the predicate by using a specialised
 438      * iterator for expressions like '/foo/bar[@attr = $var]', which are
 439      * very common in many stylesheets
 440      */
 441     public boolean isNodeValueTest() {
 442         if (!_canOptimize) return false;
 443         return (getStep() != null && getCompareValue() != null);
 444     }
 445 
 446    /**
 447      * Returns the step in an expression of the form 'step = value'.
 448      * Null is returned if the expression is not of the right form.
 449      * Optimization if off if null is returned.
 450      */
 451     public Step getStep() {
 452         // Returned cached value if called more than once
 453         if (_step != null) {
 454             return _step;
 455         }
 456 
 457         // Nothing to do if _exp is null
 458         if (_exp == null) {
 459             return null;
 460         }
 461 
 462         // Ignore if not equality expression
 463         if (_exp instanceof EqualityExpr) {
 464             EqualityExpr exp = (EqualityExpr)_exp;
 465             Expression left = exp.getLeft();
 466             Expression right = exp.getRight();
 467 
 468             // Unwrap and set _step if appropriate
 469             if (left instanceof CastExpr) {
 470                 left = ((CastExpr) left).getExpr();
 471             }
 472             if (left instanceof Step) {
 473                 _step = (Step) left;
 474             }
 475 
 476             // Unwrap and set _step if appropriate
 477             if (right instanceof CastExpr) {
 478                 right = ((CastExpr)right).getExpr();
 479             }
 480             if (right instanceof Step) {
 481                 _step = (Step)right;
 482             }
 483         }
 484         return _step;
 485     }
 486 
 487     /**
 488      * Returns the value in an expression of the form 'step = value'.
 489      * A value may be either a literal string or a variable whose
 490      * type is string. Optimization if off if null is returned.
 491      */
 492     public Expression getCompareValue() {
 493         // Returned cached value if called more than once
 494         if (_value != null) {
 495             return _value;
 496         }
 497 
 498         // Nothing to to do if _exp is null
 499         if (_exp == null) {
 500             return null;
 501         }
 502 
 503         // Ignore if not an equality expression
 504         if (_exp instanceof EqualityExpr) {
 505             EqualityExpr exp = (EqualityExpr) _exp;
 506             Expression left = exp.getLeft();
 507             Expression right = exp.getRight();
 508 
 509             // Return if left is literal string
 510             if (left instanceof LiteralExpr) {
 511                 _value = left;
 512                 return _value;
 513             }
 514             // Return if left is a variable reference of type string
 515             if (left instanceof VariableRefBase &&
 516                 left.getType() == Type.String)
 517             {
 518                 _value = left;
 519                 return _value;
 520             }
 521 
 522             // Return if right is literal string
 523             if (right instanceof LiteralExpr) {
 524                 _value = right;
 525                 return _value;
 526             }
 527             // Return if left is a variable reference whose type is string
 528             if (right instanceof VariableRefBase &&
 529                 right.getType() == Type.String)
 530             {
 531                 _value = right;
 532                 return _value;
 533             }
 534         }
 535         return null;
 536     }
 537 
 538     /**
 539      * Translate a predicate expression. This translation pushes
 540      * two references on the stack: a reference to a newly created
 541      * filter object and a reference to the predicate's closure.
 542      */
 543     public void translateFilter(ClassGenerator classGen,
 544                                 MethodGenerator methodGen)
 545     {
 546         final ConstantPoolGen cpg = classGen.getConstantPool();
 547         final InstructionList il = methodGen.getInstructionList();
 548 
 549         // Compile auxiliary class for filter
 550         compileFilter(classGen, methodGen);
 551 
 552         // Create new instance of filter
 553         il.append(new NEW(cpg.addClass(_className)));
 554         il.append(DUP);
 555         il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
 556                                                      "<init>", "()V")));
 557 
 558         // Initialize closure variables
 559         final int length = (_closureVars == null) ? 0 : _closureVars.size();
 560 
 561         for (int i = 0; i < length; i++) {
 562             VariableRefBase varRef = _closureVars.get(i);
 563             VariableBase var = varRef.getVariable();
 564             Type varType = var.getType();
 565 
 566             il.append(DUP);
 567 
 568             // Find nearest closure implemented as an inner class
 569             Closure variableClosure = _parentClosure;
 570             while (variableClosure != null) {
 571                 if (variableClosure.inInnerClass()) break;
 572                 variableClosure = variableClosure.getParentClosure();
 573             }
 574 
 575             // Use getfield if in an inner class
 576             if (variableClosure != null) {
 577                 il.append(ALOAD_0);
 578                 il.append(new GETFIELD(
 579                     cpg.addFieldref(variableClosure.getInnerClassName(),
 580                         var.getEscapedName(), varType.toSignature())));
 581             }
 582             else {
 583                 // Use a load of instruction if in translet class
 584                 il.append(var.loadInstruction());
 585             }
 586 
 587             // Store variable in new closure
 588             il.append(new PUTFIELD(
 589                     cpg.addFieldref(_className, var.getEscapedName(),
 590                         varType.toSignature())));
 591         }
 592     }
 593 
 594     /**
 595      * Translate a predicate expression. If non of the optimizations apply
 596      * then this translation pushes two references on the stack: a reference
 597      * to a newly created filter object and a reference to the predicate's
 598      * closure. See class <code>Step</code> for further details.
 599      */
 600     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 601 
 602         final ConstantPoolGen cpg = classGen.getConstantPool();
 603         final InstructionList il = methodGen.getInstructionList();
 604 
 605         if (_nthPositionFilter || _nthDescendant) {
 606             _exp.translate(classGen, methodGen);
 607         }
 608         else if (isNodeValueTest() && (getParent() instanceof Step)) {
 609             _value.translate(classGen, methodGen);
 610             il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
 611             il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
 612         }
 613         else {
 614             translateFilter(classGen, methodGen);
 615         }
 616     }
 617 }