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: RelationalExpr.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.BranchInstruction;
025 import org.apache.bcel.generic.ConstantPoolGen;
026 import org.apache.bcel.generic.INVOKESTATIC;
027 import org.apache.bcel.generic.InstructionList;
028 import org.apache.bcel.generic.PUSH;
029 import org.apache.xalan.xsltc.compiler.util.BooleanType;
030 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
032 import org.apache.xalan.xsltc.compiler.util.IntType;
033 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
034 import org.apache.xalan.xsltc.compiler.util.MethodType;
035 import org.apache.xalan.xsltc.compiler.util.NodeSetType;
036 import org.apache.xalan.xsltc.compiler.util.NodeType;
037 import org.apache.xalan.xsltc.compiler.util.RealType;
038 import org.apache.xalan.xsltc.compiler.util.ReferenceType;
039 import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
040 import org.apache.xalan.xsltc.compiler.util.Type;
041 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
042 import org.apache.xalan.xsltc.runtime.Operators;
043
044 /**
045 * @author Jacek Ambroziak
046 * @author Santiago Pericas-Geertsen
047 */
048 final class RelationalExpr extends Expression {
049
050 private int _op;
051 private Expression _left, _right;
052
053 public RelationalExpr(int op, Expression left, Expression right) {
054 _op = op;
055 (_left = left).setParent(this);
056 (_right = right).setParent(this);
057 }
058
059 public void setParser(Parser parser) {
060 super.setParser(parser);
061 _left.setParser(parser);
062 _right.setParser(parser);
063 }
064
065 /**
066 * Returns true if this expressions contains a call to position(). This is
067 * needed for context changes in node steps containing multiple predicates.
068 */
069 public boolean hasPositionCall() {
070 if (_left.hasPositionCall()) return true;
071 if (_right.hasPositionCall()) return true;
072 return false;
073 }
074
075 /**
076 * Returns true if this expressions contains a call to last()
077 */
078 public boolean hasLastCall() {
079 return (_left.hasLastCall() || _right.hasLastCall());
080 }
081
082 public boolean hasReferenceArgs() {
083 return _left.getType() instanceof ReferenceType ||
084 _right.getType() instanceof ReferenceType;
085 }
086
087 public boolean hasNodeArgs() {
088 return _left.getType() instanceof NodeType ||
089 _right.getType() instanceof NodeType;
090 }
091
092 public boolean hasNodeSetArgs() {
093 return _left.getType() instanceof NodeSetType ||
094 _right.getType() instanceof NodeSetType;
095 }
096
097 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
098 Type tleft = _left.typeCheck(stable);
099 Type tright = _right.typeCheck(stable);
100
101 //bug fix # 2838, cast to reals if both are result tree fragments
102 if (tleft instanceof ResultTreeType &&
103 tright instanceof ResultTreeType )
104 {
105 _right = new CastExpr(_right, Type.Real);
106 _left = new CastExpr(_left, Type.Real);
107 return _type = Type.Boolean;
108 }
109
110 // If one is of reference type, then convert the other too
111 if (hasReferenceArgs()) {
112 Type type = null;
113 Type typeL = null;
114 Type typeR = null;
115 if (tleft instanceof ReferenceType) {
116 if (_left instanceof VariableRefBase) {
117 VariableRefBase ref = (VariableRefBase)_left;
118 VariableBase var = ref.getVariable();
119 typeL = var.getType();
120 }
121 }
122 if (tright instanceof ReferenceType) {
123 if (_right instanceof VariableRefBase) {
124 VariableRefBase ref = (VariableRefBase)_right;
125 VariableBase var = ref.getVariable();
126 typeR = var.getType();
127 }
128 }
129 // bug fix # 2838
130 if (typeL == null)
131 type = typeR;
132 else if (typeR == null)
133 type = typeL;
134 else {
135 type = Type.Real;
136 }
137 if (type == null) type = Type.Real;
138
139 _right = new CastExpr(_right, type);
140 _left = new CastExpr(_left, type);
141 return _type = Type.Boolean;
142 }
143
144 if (hasNodeSetArgs()) {
145 // Ensure that the node-set is the left argument
146 if (tright instanceof NodeSetType) {
147 final Expression temp = _right; _right = _left; _left = temp;
148 _op = (_op == Operators.GT) ? Operators.LT :
149 (_op == Operators.LT) ? Operators.GT :
150 (_op == Operators.GE) ? Operators.LE : Operators.GE;
151 tright = _right.getType();
152 }
153
154 // Promote nodes to node sets
155 if (tright instanceof NodeType) {
156 _right = new CastExpr(_right, Type.NodeSet);
157 }
158 // Promote integer to doubles to have fewer compares
159 if (tright instanceof IntType) {
160 _right = new CastExpr(_right, Type.Real);
161 }
162 // Promote result-trees to strings
163 if (tright instanceof ResultTreeType) {
164 _right = new CastExpr(_right, Type.String);
165 }
166 return _type = Type.Boolean;
167 }
168
169 // In the node-boolean case, convert node to boolean first
170 if (hasNodeArgs()) {
171 if (tleft instanceof BooleanType) {
172 _right = new CastExpr(_right, Type.Boolean);
173 tright = Type.Boolean;
174 }
175 if (tright instanceof BooleanType) {
176 _left = new CastExpr(_left, Type.Boolean);
177 tleft = Type.Boolean;
178 }
179 }
180
181 // Lookup the table of primops to find the best match
182 MethodType ptype = lookupPrimop(stable, Operators.getOpNames(_op),
183 new MethodType(Type.Void, tleft, tright));
184
185 if (ptype != null) {
186 Type arg1 = (Type) ptype.argsType().elementAt(0);
187 if (!arg1.identicalTo(tleft)) {
188 _left = new CastExpr(_left, arg1);
189 }
190 Type arg2 = (Type) ptype.argsType().elementAt(1);
191 if (!arg2.identicalTo(tright)) {
192 _right = new CastExpr(_right, arg1);
193 }
194 return _type = ptype.resultType();
195 }
196 throw new TypeCheckError(this);
197 }
198
199 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
200 if (hasNodeSetArgs() || hasReferenceArgs()) {
201 final ConstantPoolGen cpg = classGen.getConstantPool();
202 final InstructionList il = methodGen.getInstructionList();
203
204 // Call compare() from the BasisLibrary
205 _left.translate(classGen, methodGen);
206 _left.startIterator(classGen, methodGen);
207 _right.translate(classGen, methodGen);
208 _right.startIterator(classGen, methodGen);
209
210 il.append(new PUSH(cpg, _op));
211 il.append(methodGen.loadDOM());
212
213 int index = cpg.addMethodref(BASIS_LIBRARY_CLASS, "compare",
214 "("
215 + _left.getType().toSignature()
216 + _right.getType().toSignature()
217 + "I"
218 + DOM_INTF_SIG
219 + ")Z");
220 il.append(new INVOKESTATIC(index));
221 }
222 else {
223 translateDesynthesized(classGen, methodGen);
224 synthesize(classGen, methodGen);
225 }
226 }
227
228 public void translateDesynthesized(ClassGenerator classGen,
229 MethodGenerator methodGen) {
230 if (hasNodeSetArgs() || hasReferenceArgs()) {
231 translate(classGen, methodGen);
232 desynthesize(classGen, methodGen);
233 }
234 else {
235 BranchInstruction bi = null;
236 final InstructionList il = methodGen.getInstructionList();
237
238 _left.translate(classGen, methodGen);
239 _right.translate(classGen, methodGen);
240
241 // TODO: optimize if one of the args is 0
242
243 boolean tozero = false;
244 Type tleft = _left.getType();
245
246 if (tleft instanceof RealType) {
247 il.append(tleft.CMP(_op == Operators.LT || _op == Operators.LE));
248 tleft = Type.Int;
249 tozero = true;
250 }
251
252 switch (_op) {
253 case Operators.LT:
254 bi = tleft.GE(tozero);
255 break;
256
257 case Operators.GT:
258 bi = tleft.LE(tozero);
259 break;
260
261 case Operators.LE:
262 bi = tleft.GT(tozero);
263 break;
264
265 case Operators.GE:
266 bi = tleft.LT(tozero);
267 break;
268
269 default:
270 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_RELAT_OP_ERR,this);
271 getParser().reportError(Constants.FATAL, msg);
272 }
273
274 _falseList.add(il.append(bi)); // must be backpatched
275 }
276 }
277
278 public String toString() {
279 return Operators.getOpNames(_op) + '(' + _left + ", " + _right + ')';
280 }
281 }