001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: EqualityExpr.java 468650 2006-10-28 07:03:30Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import org.apache.bcel.generic.BranchHandle; 025 import org.apache.bcel.generic.BranchInstruction; 026 import org.apache.bcel.generic.ConstantPoolGen; 027 import org.apache.bcel.generic.GOTO; 028 import org.apache.bcel.generic.IFEQ; 029 import org.apache.bcel.generic.IFNE; 030 import org.apache.bcel.generic.IF_ICMPEQ; 031 import org.apache.bcel.generic.IF_ICMPNE; 032 import org.apache.bcel.generic.INVOKESTATIC; 033 import org.apache.bcel.generic.INVOKEVIRTUAL; 034 import org.apache.bcel.generic.InstructionList; 035 import org.apache.bcel.generic.PUSH; 036 import org.apache.xalan.xsltc.compiler.util.BooleanType; 037 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 038 import org.apache.xalan.xsltc.compiler.util.IntType; 039 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 040 import org.apache.xalan.xsltc.compiler.util.NodeSetType; 041 import org.apache.xalan.xsltc.compiler.util.NodeType; 042 import org.apache.xalan.xsltc.compiler.util.NumberType; 043 import org.apache.xalan.xsltc.compiler.util.RealType; 044 import org.apache.xalan.xsltc.compiler.util.ReferenceType; 045 import org.apache.xalan.xsltc.compiler.util.ResultTreeType; 046 import org.apache.xalan.xsltc.compiler.util.StringType; 047 import org.apache.xalan.xsltc.compiler.util.Type; 048 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 049 import org.apache.xalan.xsltc.runtime.Operators; 050 051 /** 052 * @author Jacek Ambroziak 053 * @author Santiago Pericas-Geertsen 054 * @author Morten Jorgensen 055 * @author Erwin Bolwidt <ejb@klomp.org> 056 */ 057 final class EqualityExpr extends Expression { 058 059 private final int _op; 060 private Expression _left; 061 private Expression _right; 062 063 public EqualityExpr(int op, Expression left, Expression right) { 064 _op = op; 065 (_left = left).setParent(this); 066 (_right = right).setParent(this); 067 } 068 069 public void setParser(Parser parser) { 070 super.setParser(parser); 071 _left.setParser(parser); 072 _right.setParser(parser); 073 } 074 075 public String toString() { 076 return Operators.getOpNames(_op) + '(' + _left + ", " + _right + ')'; 077 } 078 079 public Expression getLeft() { 080 return _left; 081 } 082 083 public Expression getRight() { 084 return _right; 085 } 086 087 public boolean getOp() { 088 return (_op != Operators.NE); 089 } 090 091 /** 092 * Returns true if this expressions contains a call to position(). This is 093 * needed for context changes in node steps containing multiple predicates. 094 */ 095 public boolean hasPositionCall() { 096 if (_left.hasPositionCall()) return true; 097 if (_right.hasPositionCall()) return true; 098 return false; 099 } 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 }