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    }