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.generic.BranchInstruction;
  25 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  26 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  27 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  28 import com.sun.org.apache.bcel.internal.generic.PUSH;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
  33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
  35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeType;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  42 import com.sun.org.apache.xalan.internal.xsltc.runtime.Operators;
  43 
  44 /**
  45  * @author Jacek Ambroziak
  46  * @author Santiago Pericas-Geertsen
  47  */
  48 final class RelationalExpr extends Expression {
  49 
  50     private int _op;
  51     private Expression _left, _right;
  52 
  53     public RelationalExpr(int op, Expression left, Expression right) {
  54         _op = op;
  55         (_left = left).setParent(this);
  56         (_right = right).setParent(this);
  57     }
  58 
  59     public void setParser(Parser parser) {
  60         super.setParser(parser);
  61         _left.setParser(parser);
  62         _right.setParser(parser);
  63     }
  64 
  65     /**
  66      * Returns true if this expressions contains a call to position(). This is
  67      * needed for context changes in node steps containing multiple predicates.
  68      */
  69     public boolean hasPositionCall() {
  70         if (_left.hasPositionCall()) return true;
  71         if (_right.hasPositionCall()) return true;
  72         return false;
  73     }
  74 
  75     /**
  76      * Returns true if this expressions contains a call to last()
  77      */
  78     public boolean hasLastCall() {
  79             return (_left.hasLastCall() || _right.hasLastCall());
  80     }
  81 
  82     public boolean hasReferenceArgs() {
  83         return _left.getType() instanceof ReferenceType ||
  84             _right.getType() instanceof ReferenceType;
  85     }
  86 
  87     public boolean hasNodeArgs() {
  88         return _left.getType() instanceof NodeType ||
  89             _right.getType() instanceof NodeType;
  90     }
  91 
  92     public boolean hasNodeSetArgs() {
  93         return _left.getType() instanceof NodeSetType ||
  94             _right.getType() instanceof NodeSetType;
  95     }
  96 
  97     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  98         Type tleft = _left.typeCheck(stable);
  99         Type tright = _right.typeCheck(stable);
 100 
 101         //bug fix # 2838, cast to reals if both are result tree fragments
 102         if (tleft instanceof ResultTreeType &&
 103             tright instanceof ResultTreeType )
 104         {
 105             _right = new CastExpr(_right, Type.Real);
 106             _left = new CastExpr(_left, Type.Real);
 107             return _type = Type.Boolean;
 108         }
 109 
 110         // If one is of reference type, then convert the other too
 111         if (hasReferenceArgs()) {
 112             Type type = null;
 113             Type typeL = null;
 114             Type typeR = null;
 115             if (tleft instanceof ReferenceType) {
 116                 if (_left instanceof VariableRefBase) {
 117                     VariableRefBase ref = (VariableRefBase)_left;
 118                     VariableBase var = ref.getVariable();
 119                     typeL = var.getType();
 120                 }
 121             }
 122             if (tright instanceof ReferenceType) {
 123                 if (_right instanceof VariableRefBase) {
 124                     VariableRefBase ref = (VariableRefBase)_right;
 125                     VariableBase var = ref.getVariable();
 126                     typeR = var.getType();
 127                 }
 128             }
 129             // bug fix # 2838
 130             if (typeL == null)
 131                 type = typeR;
 132             else if (typeR == null)
 133                 type = typeL;
 134             else {
 135                 type = Type.Real;
 136             }
 137             if (type == null) type = Type.Real;
 138 
 139             _right = new CastExpr(_right, type);
 140             _left = new CastExpr(_left, type);
 141             return _type = Type.Boolean;
 142         }
 143 
 144         if (hasNodeSetArgs()) {
 145             // Ensure that the node-set is the left argument
 146             if (tright instanceof NodeSetType) {
 147                 final Expression temp = _right; _right = _left; _left = temp;
 148         _op = (_op == Operators.GT) ? Operators.LT :
 149             (_op == Operators.LT) ? Operators.GT :
 150             (_op == Operators.GE) ? Operators.LE : Operators.GE;
 151                 tright = _right.getType();
 152             }
 153 
 154             // Promote nodes to node sets
 155             if (tright instanceof NodeType) {
 156                 _right = new CastExpr(_right, Type.NodeSet);
 157             }
 158             // Promote integer to doubles to have fewer compares
 159             if (tright instanceof IntType) {
 160                 _right = new CastExpr(_right, Type.Real);
 161             }
 162             // Promote result-trees to strings
 163             if (tright instanceof ResultTreeType) {
 164                 _right = new CastExpr(_right, Type.String);
 165             }
 166             return _type = Type.Boolean;
 167         }
 168 
 169         // In the node-boolean case, convert node to boolean first
 170         if (hasNodeArgs()) {
 171             if (tleft instanceof BooleanType) {
 172                 _right = new CastExpr(_right, Type.Boolean);
 173                 tright = Type.Boolean;
 174             }
 175             if (tright instanceof BooleanType) {
 176                 _left = new CastExpr(_left, Type.Boolean);
 177                 tleft = Type.Boolean;
 178             }
 179         }
 180 
 181         // Lookup the table of primops to find the best match
 182     MethodType ptype = lookupPrimop(stable, Operators.getOpNames(_op),
 183                 new MethodType(Type.Void, tleft, tright));
 184 
 185         if (ptype != null) {
 186             Type arg1 = ptype.argsType().get(0);
 187             if (!arg1.identicalTo(tleft)) {
 188                 _left = new CastExpr(_left, arg1);
 189             }
 190             Type arg2 = ptype.argsType().get(1);
 191             if (!arg2.identicalTo(tright)) {
 192                 _right = new CastExpr(_right, arg1);
 193             }
 194             return _type = ptype.resultType();
 195         }
 196         throw new TypeCheckError(this);
 197     }
 198 
 199     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 200         if (hasNodeSetArgs() || hasReferenceArgs()) {
 201             final ConstantPoolGen cpg = classGen.getConstantPool();
 202             final InstructionList il = methodGen.getInstructionList();
 203 
 204             // Call compare() from the BasisLibrary
 205             _left.translate(classGen, methodGen);
 206             _left.startIterator(classGen, methodGen);
 207             _right.translate(classGen, methodGen);
 208             _right.startIterator(classGen, methodGen);
 209 
 210             il.append(new PUSH(cpg, _op));
 211             il.append(methodGen.loadDOM());
 212 
 213             int index = cpg.addMethodref(BASIS_LIBRARY_CLASS, "compare",
 214                                          "("
 215                                          + _left.getType().toSignature()
 216                                          + _right.getType().toSignature()
 217                                          + "I"
 218                                          + DOM_INTF_SIG
 219                                          + ")Z");
 220             il.append(new INVOKESTATIC(index));
 221         }
 222         else {
 223             translateDesynthesized(classGen, methodGen);
 224             synthesize(classGen, methodGen);
 225         }
 226     }
 227 
 228     public void translateDesynthesized(ClassGenerator classGen,
 229                                        MethodGenerator methodGen) {
 230         if (hasNodeSetArgs() || hasReferenceArgs()) {
 231             translate(classGen, methodGen);
 232             desynthesize(classGen, methodGen);
 233         }
 234         else {
 235             BranchInstruction bi = null;
 236             final InstructionList il = methodGen.getInstructionList();
 237 
 238             _left.translate(classGen, methodGen);
 239             _right.translate(classGen, methodGen);
 240 
 241             // TODO: optimize if one of the args is 0
 242 
 243             boolean tozero = false;
 244             Type tleft = _left.getType();
 245 
 246             if (tleft instanceof RealType) {
 247         il.append(tleft.CMP(_op == Operators.LT || _op == Operators.LE));
 248                 tleft = Type.Int;
 249                 tozero = true;
 250             }
 251 
 252             switch (_op) {
 253         case Operators.LT:
 254                 bi = tleft.GE(tozero);
 255                 break;
 256 
 257         case Operators.GT:
 258                 bi = tleft.LE(tozero);
 259                 break;
 260 
 261         case Operators.LE:
 262                 bi = tleft.GT(tozero);
 263                 break;
 264 
 265         case Operators.GE:
 266                 bi = tleft.LT(tozero);
 267                 break;
 268 
 269             default:
 270                 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_RELAT_OP_ERR,this);
 271                 getParser().reportError(Constants.FATAL, msg);
 272             }
 273 
 274             _falseList.add(il.append(bi));              // must be backpatched
 275         }
 276     }
 277 
 278     public String toString() {
 279         return Operators.getOpNames(_op) + '(' + _left + ", " + _right + ')';
 280     }
 281 }