1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   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.BranchHandle;
  25 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
  26 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  27 import com.sun.org.apache.bcel.internal.generic.GOTO;
  28 import com.sun.org.apache.bcel.internal.generic.IFEQ;
  29 import com.sun.org.apache.bcel.internal.generic.IFNE;
  30 import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ;
  31 import com.sun.org.apache.bcel.internal.generic.IF_ICMPNE;
  32 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  33 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  34 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  35 import com.sun.org.apache.bcel.internal.generic.PUSH;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  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.NodeSetType;
  41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeType;
  42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
  43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
  44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
  47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  49 import com.sun.org.apache.xalan.internal.xsltc.runtime.Operators;
  50 
  51 /**
  52  * @author Jacek Ambroziak
  53  * @author Santiago Pericas-Geertsen
  54  * @author Morten Jorgensen
  55  * @author Erwin Bolwidt <ejb@klomp.org>
  56  */
  57 final class EqualityExpr extends Expression {
  58 
  59     private final int _op;
  60     private Expression _left;
  61     private Expression _right;
  62 
  63     public EqualityExpr(int op, Expression left, Expression right) {
  64         _op = op;
  65         (_left = left).setParent(this);
  66         (_right = right).setParent(this);
  67     }
  68 
  69     public void setParser(Parser parser) {
  70         super.setParser(parser);
  71         _left.setParser(parser);
  72         _right.setParser(parser);
  73     }
  74 
  75     public String toString() {
  76         return Operators.getOpNames(_op) + '(' + _left + ", " + _right + ')';
  77     }
  78 
  79     public Expression getLeft() {
  80         return _left;
  81     }
  82 
  83     public Expression getRight() {
  84         return _right;
  85     }
  86 
  87     public boolean getOp() {
  88         return (_op != Operators.NE);
  89     }
  90 
  91     /**
  92      * Returns true if this expressions contains a call to position(). This is
  93      * needed for context changes in node steps containing multiple predicates.
  94      */
  95     public boolean hasPositionCall() {
  96         if (_left.hasPositionCall()) return true;
  97         if (_right.hasPositionCall()) return true;
  98         return false;
  99     }
 100 
 101     public boolean hasLastCall() {
 102         if (_left.hasLastCall()) return true;
 103         if (_right.hasLastCall()) return true;
 104         return false;
 105     }
 106 
 107     private void swapArguments() {
 108         final Expression temp = _left;
 109         _left = _right;
 110         _right = temp;
 111     }
 112 
 113     /**
 114      * Typing rules: see XSLT Reference by M. Kay page 345.
 115      */
 116     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 117         final Type tleft = _left.typeCheck(stable);
 118         final Type tright = _right.typeCheck(stable);
 119 
 120         if (tleft.isSimple() && tright.isSimple()) {
 121             if (tleft != tright) {
 122                 if (tleft instanceof BooleanType) {
 123                     _right = new CastExpr(_right, Type.Boolean);
 124                 }
 125                 else if (tright instanceof BooleanType) {
 126                     _left = new CastExpr(_left, Type.Boolean);
 127                 }
 128                 else if (tleft instanceof NumberType ||
 129                          tright instanceof NumberType) {
 130                     _left = new CastExpr(_left, Type.Real);
 131                     _right = new CastExpr(_right, Type.Real);
 132                 }
 133                 else {          // both compared as strings
 134                     _left = new CastExpr(_left,   Type.String);
 135                     _right = new CastExpr(_right, Type.String);
 136                 }
 137             }
 138         }
 139         else if (tleft instanceof ReferenceType) {
 140             _right = new CastExpr(_right, Type.Reference);
 141         }
 142         else if (tright instanceof ReferenceType) {
 143             _left = new CastExpr(_left, Type.Reference);
 144         }
 145         // the following 2 cases optimize @attr|.|.. = 'string'
 146         else if (tleft instanceof NodeType && tright == Type.String) {
 147             _left = new CastExpr(_left, Type.String);
 148         }
 149         else if (tleft == Type.String && tright instanceof NodeType) {
 150             _right = new CastExpr(_right, Type.String);
 151         }
 152         // optimize node/node
 153         else if (tleft instanceof NodeType && tright instanceof NodeType) {
 154             _left = new CastExpr(_left, Type.String);
 155             _right = new CastExpr(_right, Type.String);
 156         }
 157         else if (tleft instanceof NodeType && tright instanceof NodeSetType) {
 158             // compare(Node, NodeSet) will be invoked
 159         }
 160         else if (tleft instanceof NodeSetType && tright instanceof NodeType) {
 161             swapArguments();    // for compare(Node, NodeSet)
 162         }
 163         else {
 164             // At least one argument is of type node, node-set or result-tree
 165 
 166             // Promote an expression of type node to node-set
 167             if (tleft instanceof NodeType) {
 168                 _left = new CastExpr(_left, Type.NodeSet);
 169             }
 170             if (tright instanceof NodeType) {
 171                 _right = new CastExpr(_right, Type.NodeSet);
 172             }
 173 
 174             // If one arg is a node-set then make it the left one
 175             if (tleft.isSimple() ||
 176                 tleft instanceof ResultTreeType &&
 177                 tright instanceof NodeSetType) {
 178                 swapArguments();
 179             }
 180 
 181             // Promote integers to doubles to have fewer compares
 182             if (_right.getType() instanceof IntType) {
 183                 _right = new CastExpr(_right, Type.Real);
 184             }
 185         }
 186         return _type = Type.Boolean;
 187     }
 188 
 189     public void translateDesynthesized(ClassGenerator classGen,
 190                                        MethodGenerator methodGen) {
 191         final Type tleft = _left.getType();
 192         final InstructionList il = methodGen.getInstructionList();
 193 
 194         if (tleft instanceof BooleanType) {
 195             _left.translate(classGen, methodGen);
 196             _right.translate(classGen, methodGen);
 197         _falseList.add(il.append(_op == Operators.EQ ?
 198                                      (BranchInstruction)new IF_ICMPNE(null) :
 199                                      (BranchInstruction)new IF_ICMPEQ(null)));
 200         }
 201         else if (tleft instanceof NumberType) {
 202             _left.translate(classGen, methodGen);
 203             _right.translate(classGen, methodGen);
 204 
 205             if (tleft instanceof RealType) {
 206                 il.append(DCMPG);
 207         _falseList.add(il.append(_op == Operators.EQ ?
 208                                          (BranchInstruction)new IFNE(null) :
 209                                          (BranchInstruction)new IFEQ(null)));
 210             }
 211             else {
 212             _falseList.add(il.append(_op == Operators.EQ ?
 213                                          (BranchInstruction)new IF_ICMPNE(null) :
 214                                          (BranchInstruction)new IF_ICMPEQ(null)));
 215             }
 216         }
 217         else {
 218             translate(classGen, methodGen);
 219             desynthesize(classGen, methodGen);
 220         }
 221     }
 222 
 223     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 224         final ConstantPoolGen cpg = classGen.getConstantPool();
 225         final InstructionList il = methodGen.getInstructionList();
 226 
 227         final Type tleft = _left.getType();
 228         Type tright = _right.getType();
 229 
 230         if (tleft instanceof BooleanType || tleft instanceof NumberType) {
 231             translateDesynthesized(classGen, methodGen);
 232             synthesize(classGen, methodGen);
 233             return;
 234         }
 235 
 236         if (tleft instanceof StringType) {
 237             final int equals = cpg.addMethodref(STRING_CLASS,
 238                                                 "equals",
 239                                                 "(" + OBJECT_SIG +")Z");
 240             _left.translate(classGen, methodGen);
 241             _right.translate(classGen, methodGen);
 242             il.append(new INVOKEVIRTUAL(equals));
 243 
 244         if (_op == Operators.NE) {
 245                 il.append(ICONST_1);
 246                 il.append(IXOR);                        // not x <-> x xor 1
 247             }
 248             return;
 249         }
 250 
 251         BranchHandle truec, falsec;
 252 
 253         if (tleft instanceof ResultTreeType) {
 254             if (tright instanceof BooleanType) {
 255                 _right.translate(classGen, methodGen);
 256         if (_op == Operators.NE) {
 257                     il.append(ICONST_1);
 258                     il.append(IXOR); // not x <-> x xor 1
 259                 }
 260                 return;
 261             }
 262 
 263             if (tright instanceof RealType) {
 264                 _left.translate(classGen, methodGen);
 265                 tleft.translateTo(classGen, methodGen, Type.Real);
 266                 _right.translate(classGen, methodGen);
 267 
 268                 il.append(DCMPG);
 269         falsec = il.append(_op == Operators.EQ ?
 270                                    (BranchInstruction) new IFNE(null) :
 271                                    (BranchInstruction) new IFEQ(null));
 272                 il.append(ICONST_1);
 273                 truec = il.append(new GOTO(null));
 274                 falsec.setTarget(il.append(ICONST_0));
 275                 truec.setTarget(il.append(NOP));
 276                 return;
 277             }
 278 
 279             // Next, result-tree/string and result-tree/result-tree comparisons
 280 
 281             _left.translate(classGen, methodGen);
 282             tleft.translateTo(classGen, methodGen, Type.String);
 283             _right.translate(classGen, methodGen);
 284 
 285             if (tright instanceof ResultTreeType) {
 286                 tright.translateTo(classGen, methodGen, Type.String);
 287             }
 288 
 289             final int equals = cpg.addMethodref(STRING_CLASS,
 290                                                 "equals",
 291                                                 "(" +OBJECT_SIG+ ")Z");
 292             il.append(new INVOKEVIRTUAL(equals));
 293 
 294         if (_op == Operators.NE) {
 295                 il.append(ICONST_1);
 296                 il.append(IXOR);                        // not x <-> x xor 1
 297             }
 298             return;
 299         }
 300 
 301         if (tleft instanceof NodeSetType && tright instanceof BooleanType) {
 302             _left.translate(classGen, methodGen);
 303             _left.startIterator(classGen, methodGen);
 304             Type.NodeSet.translateTo(classGen, methodGen, Type.Boolean);
 305             _right.translate(classGen, methodGen);
 306 
 307             il.append(IXOR); // x != y <-> x xor y
 308         if (_op == Operators.EQ) {
 309                 il.append(ICONST_1);
 310                 il.append(IXOR); // not x <-> x xor 1
 311             }
 312             return;
 313         }
 314 
 315         if (tleft instanceof NodeSetType && tright instanceof StringType) {
 316             _left.translate(classGen, methodGen);
 317             _left.startIterator(classGen, methodGen); // needed ?
 318             _right.translate(classGen, methodGen);
 319             il.append(new PUSH(cpg, _op));
 320             il.append(methodGen.loadDOM());
 321             final int cmp = cpg.addMethodref(BASIS_LIBRARY_CLASS,
 322                                              "compare",
 323                                              "("
 324                                              + tleft.toSignature()
 325                                              + tright.toSignature()
 326                                              + "I"
 327                                              + DOM_INTF_SIG
 328                                              + ")Z");
 329             il.append(new INVOKESTATIC(cmp));
 330             return;
 331         }
 332 
 333         // Next, node-set/t for t in {real, string, node-set, result-tree}
 334         _left.translate(classGen, methodGen);
 335         _left.startIterator(classGen, methodGen);
 336         _right.translate(classGen, methodGen);
 337         _right.startIterator(classGen, methodGen);
 338 
 339         // Cast a result tree to a string to use an existing compare
 340         if (tright instanceof ResultTreeType) {
 341             tright.translateTo(classGen, methodGen, Type.String);
 342             tright = Type.String;
 343         }
 344 
 345         // Call the appropriate compare() from the BasisLibrary
 346         il.append(new PUSH(cpg, _op));
 347         il.append(methodGen.loadDOM());
 348 
 349         final int compare = cpg.addMethodref(BASIS_LIBRARY_CLASS,
 350                                              "compare",
 351                                              "("
 352                                              + tleft.toSignature()
 353                                              + tright.toSignature()
 354                                              + "I"
 355                                              + DOM_INTF_SIG
 356                                              + ")Z");
 357         il.append(new INVOKESTATIC(compare));
 358     }
 359 }