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
22package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24import com.sun.org.apache.bcel.internal.generic.BranchHandle;
25import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
26import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
27import com.sun.org.apache.bcel.internal.generic.GOTO;
28import com.sun.org.apache.bcel.internal.generic.IFEQ;
29import com.sun.org.apache.bcel.internal.generic.IFNE;
30import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ;
31import com.sun.org.apache.bcel.internal.generic.IF_ICMPNE;
32import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
33import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
34import com.sun.org.apache.bcel.internal.generic.InstructionList;
35import com.sun.org.apache.bcel.internal.generic.PUSH;
36import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
37import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
38import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
39import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
40import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
41import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeType;
42import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
43import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
44import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
45import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
46import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
47import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
48import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
49import 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 */
57final 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}
360