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: FilterExpr.java 1225842 2011-12-30 15:14:35Z mrglavas $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.util.Vector; 025 026 import org.apache.bcel.generic.ALOAD; 027 import org.apache.bcel.generic.ASTORE; 028 import org.apache.bcel.generic.ConstantPoolGen; 029 import org.apache.bcel.generic.ILOAD; 030 import org.apache.bcel.generic.INVOKESPECIAL; 031 import org.apache.bcel.generic.ISTORE; 032 import org.apache.bcel.generic.InstructionList; 033 import org.apache.bcel.generic.LocalVariableGen; 034 import org.apache.bcel.generic.NEW; 035 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 036 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 037 import org.apache.xalan.xsltc.compiler.util.NodeSetType; 038 import org.apache.xalan.xsltc.compiler.util.ReferenceType; 039 import org.apache.xalan.xsltc.compiler.util.Type; 040 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 041 import org.apache.xalan.xsltc.compiler.util.Util; 042 043 /** 044 * @author Jacek Ambroziak 045 * @author Santiago Pericas-Geertsen 046 * @author Morten Jorgensen 047 */ 048 class FilterExpr extends Expression { 049 050 /** 051 * Primary expression of this filter. I.e., 'e' in '(e)[p1]...[pn]'. 052 */ 053 private Expression _primary; 054 055 /** 056 * Array of predicates in '(e)[p1]...[pn]'. 057 */ 058 private final Vector _predicates; 059 060 public FilterExpr(Expression primary, Vector predicates) { 061 _primary = primary; 062 _predicates = predicates; 063 primary.setParent(this); 064 } 065 066 protected Expression getExpr() { 067 if (_primary instanceof CastExpr) 068 return ((CastExpr)_primary).getExpr(); 069 else 070 return _primary; 071 } 072 073 public void setParser(Parser parser) { 074 super.setParser(parser); 075 _primary.setParser(parser); 076 if (_predicates != null) { 077 final int n = _predicates.size(); 078 for (int i = 0; i < n; i++) { 079 final Expression exp = (Expression)_predicates.elementAt(i); 080 exp.setParser(parser); 081 exp.setParent(this); 082 } 083 } 084 } 085 086 public String toString() { 087 return "filter-expr(" + _primary + ", " + _predicates + ")"; 088 } 089 090 /** 091 * Type check a FilterParentPath. If the filter is not a node-set add a 092 * cast to node-set only if it is of reference type. This type coercion 093 * is needed for expressions like $x where $x is a parameter reference. 094 * All optimizations are turned off before type checking underlying 095 * predicates. 096 */ 097 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 098 Type ptype = _primary.typeCheck(stable); 099 boolean canOptimize = _primary instanceof KeyCall; 100 101 if (ptype instanceof NodeSetType == false) { 102 if (ptype instanceof ReferenceType) { 103 _primary = new CastExpr(_primary, Type.NodeSet); 104 } 105 else { 106 throw new TypeCheckError(this); 107 } 108 } 109 110 // Type check predicates and turn all optimizations off if appropriate 111 int n = _predicates.size(); 112 for (int i = 0; i < n; i++) { 113 Predicate pred = (Predicate) _predicates.elementAt(i); 114 115 if (!canOptimize) { 116 pred.dontOptimize(); 117 } 118 pred.typeCheck(stable); 119 } 120 return _type = Type.NodeSet; 121 } 122 123 /** 124 * Translate a filter expression by pushing the appropriate iterator 125 * onto the stack. 126 */ 127 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 128 if (_predicates.size() > 0) { 129 translatePredicates(classGen, methodGen); 130 } 131 else { 132 _primary.translate(classGen, methodGen); 133 } 134 } 135 136 /** 137 * Translate a sequence of predicates. Each predicate is translated 138 * by constructing an instance of <code>CurrentNodeListIterator</code> 139 * which is initialized from another iterator (recursive call), a 140 * filter and a closure (call to translate on the predicate) and "this". 141 */ 142 public void translatePredicates(ClassGenerator classGen, 143 MethodGenerator methodGen) { 144 final ConstantPoolGen cpg = classGen.getConstantPool(); 145 final InstructionList il = methodGen.getInstructionList(); 146 147 // If not predicates left, translate primary expression 148 if (_predicates.size() == 0) { 149 translate(classGen, methodGen); 150 } else { 151 // Remove the next predicate to be translated 152 Predicate predicate = (Predicate)_predicates.lastElement(); 153 _predicates.remove(predicate); 154 155 // Translate the rest of the predicates from right to left 156 translatePredicates(classGen, methodGen); 157 158 if (predicate.isNthPositionFilter()) { 159 int nthIteratorIdx = cpg.addMethodref(NTH_ITERATOR_CLASS, 160 "<init>", 161 "("+NODE_ITERATOR_SIG+"I)V"); 162 163 // Backwards branches are prohibited if an uninitialized object 164 // is on the stack by section 4.9.4 of the JVM Specification, 165 // 2nd Ed. We don't know whether this code might contain 166 // backwards branches, so we mustn't create the new object unti 167 168 // after we've created the suspect arguments to its constructor 169 170 // Instead we calculate the values of the arguments to the 171 // constructor first, store them in temporary variables, create 172 // the object and reload the arguments from the temporaries to 173 // avoid the problem. 174 LocalVariableGen iteratorTemp 175 = methodGen.addLocalVariable("filter_expr_tmp1", 176 Util.getJCRefType(NODE_ITERATOR_SIG), 177 null, null); 178 iteratorTemp.setStart( 179 il.append(new ASTORE(iteratorTemp.getIndex()))); 180 181 predicate.translate(classGen, methodGen); 182 LocalVariableGen predicateValueTemp 183 = methodGen.addLocalVariable("filter_expr_tmp2", 184 Util.getJCRefType("I"), 185 null, null); 186 predicateValueTemp.setStart( 187 il.append(new ISTORE(predicateValueTemp.getIndex()))); 188 189 il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS))); 190 il.append(DUP); 191 iteratorTemp.setEnd( 192 il.append(new ALOAD(iteratorTemp.getIndex()))); 193 predicateValueTemp.setEnd( 194 il.append(new ILOAD(predicateValueTemp.getIndex()))); 195 il.append(new INVOKESPECIAL(nthIteratorIdx)); 196 } else { 197 // Translate predicates from right to left 198 final int initCNLI = 199 cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR, 200 "<init>", 201 "("+NODE_ITERATOR_SIG+"Z"+ 202 CURRENT_NODE_LIST_FILTER_SIG + 203 NODE_SIG+TRANSLET_SIG+")V"); 204 205 // Backwards branches are prohibited if an uninitialized object 206 // is on the stack by section 4.9.4 of the JVM Specification, 207 // 2nd Ed. We don't know whether this code might contain 208 // backwards branches, so we mustn't create the new object 209 // until after we've created the suspect arguments to its 210 // constructor. Instead we calculate the values of the 211 // arguments to the constructor first, store them in temporary 212 // variables, create the object and reload the arguments from 213 // the temporaries to avoid the problem. 214 215 216 LocalVariableGen nodeIteratorTemp = 217 methodGen.addLocalVariable("filter_expr_tmp1", 218 Util.getJCRefType(NODE_ITERATOR_SIG), 219 null, null); 220 nodeIteratorTemp.setStart( 221 il.append(new ASTORE(nodeIteratorTemp.getIndex()))); 222 223 predicate.translate(classGen, methodGen); 224 LocalVariableGen filterTemp = 225 methodGen.addLocalVariable("filter_expr_tmp2", 226 Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG), 227 null, null); 228 filterTemp.setStart( 229 il.append(new ASTORE(filterTemp.getIndex()))); 230 231 // Create a CurrentNodeListIterator 232 il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR))); 233 il.append(DUP); 234 235 // Initialize CurrentNodeListIterator 236 nodeIteratorTemp.setEnd( 237 il.append(new ALOAD(nodeIteratorTemp.getIndex()))); 238 il.append(ICONST_1); 239 filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex()))); 240 il.append(methodGen.loadCurrentNode()); 241 il.append(classGen.loadTranslet()); 242 il.append(new INVOKESPECIAL(initCNLI)); 243 } 244 } 245 } 246 }