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 }