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: Expression.java 468650 2006-10-28 07:03:30Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import java.util.Vector;
025
026 import org.apache.bcel.generic.BranchHandle;
027 import org.apache.bcel.generic.ConstantPoolGen;
028 import org.apache.bcel.generic.GOTO_W;
029 import org.apache.bcel.generic.IFEQ;
030 import org.apache.bcel.generic.InstructionHandle;
031 import org.apache.bcel.generic.InstructionList;
032 import org.apache.xalan.xsltc.compiler.util.BooleanType;
033 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
034 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
035 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
036 import org.apache.xalan.xsltc.compiler.util.MethodType;
037 import org.apache.xalan.xsltc.compiler.util.NodeSetType;
038 import org.apache.xalan.xsltc.compiler.util.Type;
039 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
040
041 /**
042 * @author Jacek Ambroziak
043 * @author Santiago Pericas-Geertsen
044 * @author Morten Jorgensen
045 * @author Erwin Bolwidt <ejb@klomp.org>
046 */
047 abstract class Expression extends SyntaxTreeNode {
048 /**
049 * The type of this expression. It is set after calling
050 * <code>typeCheck()</code>.
051 */
052 protected Type _type;
053
054 /**
055 * Instruction handles that comprise the true list.
056 */
057 protected FlowList _trueList = new FlowList();
058
059 /**
060 * Instruction handles that comprise the false list.
061 */
062 protected FlowList _falseList = new FlowList();
063
064 public Type getType() {
065 return _type;
066 }
067
068 public abstract String toString();
069
070 public boolean hasPositionCall() {
071 return false; // default should be 'false' for StepPattern
072 }
073
074 public boolean hasLastCall() {
075 return false;
076 }
077
078 /**
079 * Returns an object representing the compile-time evaluation
080 * of an expression. We are only using this for function-available
081 * and element-available at this time.
082 */
083 public Object evaluateAtCompileTime() {
084 return null;
085 }
086
087 /**
088 * Type check all the children of this node.
089 */
090 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
091 return typeCheckContents(stable);
092 }
093
094 /**
095 * Translate this node into JVM bytecodes.
096 */
097 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
098 ErrorMsg msg = new ErrorMsg(ErrorMsg.NOT_IMPLEMENTED_ERR,
099 getClass(), this);
100 getParser().reportError(FATAL, msg);
101 }
102
103 /**
104 * Translate this node into a fresh instruction list.
105 * The original instruction list is saved and restored.
106 */
107 public final InstructionList compile(ClassGenerator classGen,
108 MethodGenerator methodGen) {
109 final InstructionList result, save = methodGen.getInstructionList();
110 methodGen.setInstructionList(result = new InstructionList());
111 translate(classGen, methodGen);
112 methodGen.setInstructionList(save);
113 return result;
114 }
115
116 /**
117 * Redefined by expressions of type boolean that use flow lists.
118 */
119 public void translateDesynthesized(ClassGenerator classGen,
120 MethodGenerator methodGen) {
121 translate(classGen, methodGen);
122 if (_type instanceof BooleanType) {
123 desynthesize(classGen, methodGen);
124 }
125 }
126
127 /**
128 * If this expression is of type node-set and it is not a variable
129 * reference, then call setStartNode() passing the context node.
130 */
131 public void startIterator(ClassGenerator classGen,
132 MethodGenerator methodGen) {
133 // Ignore if type is not node-set
134 if (_type instanceof NodeSetType == false) {
135 return;
136 }
137
138 // setStartNode() should not be called if expr is a variable ref
139 Expression expr = this;
140 if (expr instanceof CastExpr) {
141 expr = ((CastExpr) expr).getExpr();
142 }
143 if (expr instanceof VariableRefBase == false) {
144 final InstructionList il = methodGen.getInstructionList();
145 il.append(methodGen.loadContextNode());
146 il.append(methodGen.setStartNode());
147 }
148 }
149
150 /**
151 * Synthesize a boolean expression, i.e., either push a 0 or 1 onto the
152 * operand stack for the next statement to succeed. Returns the handle
153 * of the instruction to be backpatched.
154 */
155 public void synthesize(ClassGenerator classGen, MethodGenerator methodGen) {
156 final ConstantPoolGen cpg = classGen.getConstantPool();
157 final InstructionList il = methodGen.getInstructionList();
158 _trueList.backPatch(il.append(ICONST_1));
159 final BranchHandle truec = il.append(new GOTO_W(null));
160 _falseList.backPatch(il.append(ICONST_0));
161 truec.setTarget(il.append(NOP));
162 }
163
164 public void desynthesize(ClassGenerator classGen,
165 MethodGenerator methodGen) {
166 final InstructionList il = methodGen.getInstructionList();
167 _falseList.add(il.append(new IFEQ(null)));
168 }
169
170 public FlowList getFalseList() {
171 return _falseList;
172 }
173
174 public FlowList getTrueList() {
175 return _trueList;
176 }
177
178 public void backPatchFalseList(InstructionHandle ih) {
179 _falseList.backPatch(ih);
180 }
181
182 public void backPatchTrueList(InstructionHandle ih) {
183 _trueList.backPatch(ih);
184 }
185
186 /**
187 * Search for a primop in the symbol table that matches the method type
188 * <code>ctype</code>. Two methods match if they have the same arity.
189 * If a primop is overloaded then the "closest match" is returned. The
190 * first entry in the vector of primops that has the right arity is
191 * considered to be the default one.
192 */
193 public MethodType lookupPrimop(SymbolTable stable, String op,
194 MethodType ctype) {
195 MethodType result = null;
196 final Vector primop = stable.lookupPrimop(op);
197 if (primop != null) {
198 final int n = primop.size();
199 int minDistance = Integer.MAX_VALUE;
200 for (int i = 0; i < n; i++) {
201 final MethodType ptype = (MethodType) primop.elementAt(i);
202 // Skip if different arity
203 if (ptype.argsCount() != ctype.argsCount()) {
204 continue;
205 }
206
207 // The first method with the right arity is the default
208 if (result == null) {
209 result = ptype; // default method
210 }
211
212 // Check if better than last one found
213 final int distance = ctype.distanceTo(ptype);
214 if (distance < minDistance) {
215 minDistance = distance;
216 result = ptype;
217 }
218 }
219 }
220 return result;
221 }
222 }