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    }