1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Nov 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.generic.GOTO;
  25 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  26 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  27 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  32 
  33 /**
  34  * @author Jacek Ambroziak
  35  * @author Santiago Pericas-Geertsen
  36  * @author Morten Jorgensen
  37  */
  38 final class LogicalExpr extends Expression {
  39 
  40     public static final int OR  = 0;
  41     public static final int AND = 1;
  42 
  43     private final int  _op;     // operator
  44     private Expression _left;   // first operand
  45     private Expression _right;  // second operand
  46 
  47     private static final String[] Ops = { "or", "and" };
  48 
  49     /**
  50      * Creates a new logical expression - either OR or AND. Note that the
  51      * left- and right-hand side expressions can also be logical expressions,
  52      * thus creating logical trees representing structures such as
  53      * (a and (b or c) and d), etc...
  54      */
  55     public LogicalExpr(int op, Expression left, Expression right) {
  56         _op = op;
  57         (_left = left).setParent(this);
  58         (_right = right).setParent(this);
  59     }
  60 
  61     /**
  62      * Returns true if this expressions contains a call to position(). This is
  63      * needed for context changes in node steps containing multiple predicates.
  64      */
  65     public boolean hasPositionCall() {
  66         return (_left.hasPositionCall() || _right.hasPositionCall());
  67     }
  68 
  69     /**
  70      * Returns true if this expressions contains a call to last()
  71      */
  72     public boolean hasLastCall() {
  73             return (_left.hasLastCall() || _right.hasLastCall());
  74     }
  75 
  76     /**
  77      * Returns an object representing the compile-time evaluation
  78      * of an expression. We are only using this for function-available
  79      * and element-available at this time.
  80      */
  81     public Object evaluateAtCompileTime() {
  82         final Object leftb = _left.evaluateAtCompileTime();
  83         final Object rightb = _right.evaluateAtCompileTime();
  84 
  85         // Return null if we can't evaluate at compile time
  86         if (leftb == null || rightb == null) {
  87             return null;
  88         }
  89 
  90         if (_op == AND) {
  91             return (leftb == Boolean.TRUE && rightb == Boolean.TRUE) ?
  92                 Boolean.TRUE : Boolean.FALSE;
  93         }
  94         else {
  95             return (leftb == Boolean.TRUE || rightb == Boolean.TRUE) ?
  96                 Boolean.TRUE : Boolean.FALSE;
  97         }
  98     }
  99 
 100     /**
 101      * Returns this logical expression's operator - OR or AND represented
 102      * by 0 and 1 respectively.
 103      */
 104     public int getOp() {
 105         return(_op);
 106     }
 107 
 108     /**
 109      * Override the SyntaxTreeNode.setParser() method to make sure that the
 110      * parser is set for sub-expressions
 111      */
 112     public void setParser(Parser parser) {
 113         super.setParser(parser);
 114         _left.setParser(parser);
 115         _right.setParser(parser);
 116     }
 117 
 118     /**
 119      * Returns a string describing this expression
 120      */
 121     public String toString() {
 122         return Ops[_op] + '(' + _left + ", " + _right + ')';
 123     }
 124 
 125     /**
 126      * Type-check this expression, and possibly child expressions.
 127      */
 128     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 129         // Get the left and right operand types
 130         Type tleft = _left.typeCheck(stable);
 131         Type tright = _right.typeCheck(stable);
 132 
 133         // Check if the operator supports the two operand types
 134         MethodType wantType = new MethodType(Type.Void, tleft, tright);
 135         MethodType haveType = lookupPrimop(stable, Ops[_op], wantType);
 136 
 137         // Yes, the operation is supported
 138         if (haveType != null) {
 139             // Check if left-hand side operand must be type casted
 140             Type arg1 = haveType.argsType().get(0);
 141             if (!arg1.identicalTo(tleft))
 142                 _left = new CastExpr(_left, arg1);
 143             // Check if right-hand side operand must be type casted
 144             Type arg2 = haveType.argsType().get(1);
 145             if (!arg2.identicalTo(tright))
 146                 _right = new CastExpr(_right, arg1);
 147             // Return the result type for the operator we will use
 148             return _type = haveType.resultType();
 149         }
 150         throw new TypeCheckError(this);
 151     }
 152 
 153     /**
 154      * Compile the expression - leave boolean expression on stack
 155      */
 156     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 157         translateDesynthesized(classGen, methodGen);
 158         synthesize(classGen, methodGen);
 159     }
 160 
 161     /**
 162      * Compile expression and update true/false-lists
 163      */
 164     public void translateDesynthesized(ClassGenerator classGen,
 165                                        MethodGenerator methodGen) {
 166 
 167         final InstructionList il = methodGen.getInstructionList();
 168         final SyntaxTreeNode parent = getParent();
 169 
 170         // Compile AND-expression
 171         if (_op == AND) {
 172 
 173             // Translate left hand side - must be true
 174             _left.translateDesynthesized(classGen, methodGen);
 175 
 176             // Need this for chaining any OR-expression children
 177             InstructionHandle middle = il.append(NOP);
 178 
 179             // Translate left right side - must be true
 180             _right.translateDesynthesized(classGen, methodGen);
 181 
 182             // Need this for chaining any OR-expression children
 183             InstructionHandle after = il.append(NOP);
 184 
 185             // Append child expression false-lists to our false-list
 186             _falseList.append(_right._falseList.append(_left._falseList));
 187 
 188             // Special case for OR-expression as a left child of AND.
 189             // The true-list of OR must point to second clause of AND.
 190             if ((_left instanceof LogicalExpr) &&
 191                 (((LogicalExpr)_left).getOp() == OR)) {
 192                 _left.backPatchTrueList(middle);
 193             }
 194             else if (_left instanceof NotCall) {
 195                 _left.backPatchTrueList(middle);
 196             }
 197             else {
 198                 _trueList.append(_left._trueList);
 199             }
 200 
 201             // Special case for OR-expression as a right child of AND
 202             // The true-list of OR must point to true-list of AND.
 203             if ((_right instanceof LogicalExpr) &&
 204                 (((LogicalExpr)_right).getOp() == OR)) {
 205                 _right.backPatchTrueList(after);
 206             }
 207             else if (_right instanceof NotCall) {
 208                 _right.backPatchTrueList(after);
 209             }
 210             else {
 211                 _trueList.append(_right._trueList);
 212             }
 213         }
 214         // Compile OR-expression
 215         else {
 216             // Translate left-hand side expression and produce true/false list
 217             _left.translateDesynthesized(classGen, methodGen);
 218 
 219             // This GOTO is used to skip over the code for the last test
 220             // in the case where the the first test succeeds
 221             InstructionHandle ih = il.append(new GOTO(null));
 222 
 223             // Translate right-hand side expression and produce true/false list
 224             _right.translateDesynthesized(classGen, methodGen);
 225 
 226             _left._trueList.backPatch(ih);
 227             _left._falseList.backPatch(ih.getNext());
 228 
 229             _falseList.append(_right._falseList);
 230             _trueList.add(ih).append(_right._trueList);
 231         }
 232     }
 233 }