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: LogicalExpr.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.GOTO;
025 import org.apache.bcel.generic.InstructionHandle;
026 import org.apache.bcel.generic.InstructionList;
027 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
028 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
029 import org.apache.xalan.xsltc.compiler.util.MethodType;
030 import org.apache.xalan.xsltc.compiler.util.Type;
031 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
032
033 /**
034 * @author Jacek Ambroziak
035 * @author Santiago Pericas-Geertsen
036 * @author Morten Jorgensen
037 */
038 final class LogicalExpr extends Expression {
039
040 public static final int OR = 0;
041 public static final int AND = 1;
042
043 private final int _op; // operator
044 private Expression _left; // first operand
045 private Expression _right; // second operand
046
047 private static final String[] Ops = { "or", "and" };
048
049 /**
050 * Creates a new logical expression - either OR or AND. Note that the
051 * left- and right-hand side expressions can also be logical expressions,
052 * thus creating logical trees representing structures such as
053 * (a and (b or c) and d), etc...
054 */
055 public LogicalExpr(int op, Expression left, Expression right) {
056 _op = op;
057 (_left = left).setParent(this);
058 (_right = right).setParent(this);
059 }
060
061 /**
062 * Returns true if this expressions contains a call to position(). This is
063 * needed for context changes in node steps containing multiple predicates.
064 */
065 public boolean hasPositionCall() {
066 return (_left.hasPositionCall() || _right.hasPositionCall());
067 }
068
069 /**
070 * Returns true if this expressions contains a call to last()
071 */
072 public boolean hasLastCall() {
073 return (_left.hasLastCall() || _right.hasLastCall());
074 }
075
076 /**
077 * Returns an object representing the compile-time evaluation
078 * of an expression. We are only using this for function-available
079 * and element-available at this time.
080 */
081 public Object evaluateAtCompileTime() {
082 final Object leftb = _left.evaluateAtCompileTime();
083 final Object rightb = _right.evaluateAtCompileTime();
084
085 // Return null if we can't evaluate at compile time
086 if (leftb == null || rightb == null) {
087 return null;
088 }
089
090 if (_op == AND) {
091 return (leftb == Boolean.TRUE && rightb == Boolean.TRUE) ?
092 Boolean.TRUE : Boolean.FALSE;
093 }
094 else {
095 return (leftb == Boolean.TRUE || rightb == Boolean.TRUE) ?
096 Boolean.TRUE : Boolean.FALSE;
097 }
098 }
099
100 /**
101 * Returns this logical expression's operator - OR or AND represented
102 * by 0 and 1 respectively.
103 */
104 public int getOp() {
105 return(_op);
106 }
107
108 /**
109 * Override the SyntaxTreeNode.setParser() method to make sure that the
110 * parser is set for sub-expressions
111 */
112 public void setParser(Parser parser) {
113 super.setParser(parser);
114 _left.setParser(parser);
115 _right.setParser(parser);
116 }
117
118 /**
119 * Returns a string describing this expression
120 */
121 public String toString() {
122 return Ops[_op] + '(' + _left + ", " + _right + ')';
123 }
124
125 /**
126 * Type-check this expression, and possibly child expressions.
127 */
128 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
129 // Get the left and right operand types
130 Type tleft = _left.typeCheck(stable);
131 Type tright = _right.typeCheck(stable);
132
133 // Check if the operator supports the two operand types
134 MethodType wantType = new MethodType(Type.Void, tleft, tright);
135 MethodType haveType = lookupPrimop(stable, Ops[_op], wantType);
136
137 // Yes, the operation is supported
138 if (haveType != null) {
139 // Check if left-hand side operand must be type casted
140 Type arg1 = (Type)haveType.argsType().elementAt(0);
141 if (!arg1.identicalTo(tleft))
142 _left = new CastExpr(_left, arg1);
143 // Check if right-hand side operand must be type casted
144 Type arg2 = (Type) haveType.argsType().elementAt(1);
145 if (!arg2.identicalTo(tright))
146 _right = new CastExpr(_right, arg1);
147 // Return the result type for the operator we will use
148 return _type = haveType.resultType();
149 }
150 throw new TypeCheckError(this);
151 }
152
153 /**
154 * Compile the expression - leave boolean expression on stack
155 */
156 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
157 translateDesynthesized(classGen, methodGen);
158 synthesize(classGen, methodGen);
159 }
160
161 /**
162 * Compile expression and update true/false-lists
163 */
164 public void translateDesynthesized(ClassGenerator classGen,
165 MethodGenerator methodGen) {
166
167 final InstructionList il = methodGen.getInstructionList();
168 final SyntaxTreeNode parent = getParent();
169
170 // Compile AND-expression
171 if (_op == AND) {
172
173 // Translate left hand side - must be true
174 _left.translateDesynthesized(classGen, methodGen);
175
176 // Need this for chaining any OR-expression children
177 InstructionHandle middle = il.append(NOP);
178
179 // Translate left right side - must be true
180 _right.translateDesynthesized(classGen, methodGen);
181
182 // Need this for chaining any OR-expression children
183 InstructionHandle after = il.append(NOP);
184
185 // Append child expression false-lists to our false-list
186 _falseList.append(_right._falseList.append(_left._falseList));
187
188 // Special case for OR-expression as a left child of AND.
189 // The true-list of OR must point to second clause of AND.
190 if ((_left instanceof LogicalExpr) &&
191 (((LogicalExpr)_left).getOp() == OR)) {
192 _left.backPatchTrueList(middle);
193 }
194 else if (_left instanceof NotCall) {
195 _left.backPatchTrueList(middle);
196 }
197 else {
198 _trueList.append(_left._trueList);
199 }
200
201 // Special case for OR-expression as a right child of AND
202 // The true-list of OR must point to true-list of AND.
203 if ((_right instanceof LogicalExpr) &&
204 (((LogicalExpr)_right).getOp() == OR)) {
205 _right.backPatchTrueList(after);
206 }
207 else if (_right instanceof NotCall) {
208 _right.backPatchTrueList(after);
209 }
210 else {
211 _trueList.append(_right._trueList);
212 }
213 }
214 // Compile OR-expression
215 else {
216 // Translate left-hand side expression and produce true/false list
217 _left.translateDesynthesized(classGen, methodGen);
218
219 // This GOTO is used to skip over the code for the last test
220 // in the case where the the first test succeeds
221 InstructionHandle ih = il.append(new GOTO(null));
222
223 // Translate right-hand side expression and produce true/false list
224 _right.translateDesynthesized(classGen, methodGen);
225
226 _left._trueList.backPatch(ih);
227 _left._falseList.backPatch(ih.getNext());
228
229 _falseList.append(_right._falseList);
230 _trueList.add(ih).append(_right._trueList);
231 }
232 }
233 }