1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xalan.internal.xsltc.compiler;
22 
23 import com.sun.org.apache.bcel.internal.generic.ALOAD;
24 import com.sun.org.apache.bcel.internal.generic.ASTORE;
25 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
26 import com.sun.org.apache.bcel.internal.generic.ILOAD;
27 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
28 import com.sun.org.apache.bcel.internal.generic.ISTORE;
29 import com.sun.org.apache.bcel.internal.generic.InstructionList;
30 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
31 import com.sun.org.apache.bcel.internal.generic.NEW;
32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
39 import java.util.List;
40 
41 /**
42  * @author Jacek Ambroziak
43  * @author Santiago Pericas-Geertsen
44  * @author Morten Jorgensen
45  * @LastModified: Nov 2017
46  */
47 class FilterExpr extends Expression {
48 
49     /**
50      * Primary expression of this filter. I.e., 'e' in '(e)[p1]...[pn]'.
51      */
52     private Expression   _primary;
53 
54     /**
55      * Array of predicates in '(e)[p1]...[pn]'.
56      */
57     private final List<Expression> _predicates;
58 
FilterExpr(Expression primary, List<Expression> predicates)59     public FilterExpr(Expression primary, List<Expression> predicates) {
60         _primary = primary;
61         _predicates = predicates;
62         primary.setParent(this);
63     }
64 
getExpr()65     protected Expression getExpr() {
66         if (_primary instanceof CastExpr)
67             return ((CastExpr)_primary).getExpr();
68         else
69             return _primary;
70     }
71 
setParser(Parser parser)72     public void setParser(Parser parser) {
73         super.setParser(parser);
74         _primary.setParser(parser);
75         if (_predicates != null) {
76             final int n = _predicates.size();
77             for (int i = 0; i < n; i++) {
78                 final Expression exp = _predicates.get(i);
79                 exp.setParser(parser);
80                 exp.setParent(this);
81             }
82         }
83     }
84 
toString()85     public String toString() {
86         return "filter-expr(" + _primary + ", " + _predicates + ")";
87     }
88 
89     /**
90      * Type check a FilterParentPath. If the filter is not a node-set add a
91      * cast to node-set only if it is of reference type. This type coercion
92      * is needed for expressions like $x where $x is a parameter reference.
93      * All optimizations are turned off before type checking underlying
94      * predicates.
95      */
typeCheck(SymbolTable stable)96     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
97         Type ptype = _primary.typeCheck(stable);
98         boolean canOptimize = _primary instanceof KeyCall;
99 
100         if (ptype instanceof NodeSetType == false) {
101             if (ptype instanceof ReferenceType)  {
102                 _primary = new CastExpr(_primary, Type.NodeSet);
103             }
104             else {
105                 throw new TypeCheckError(this);
106             }
107         }
108 
109         // Type check predicates and turn all optimizations off if appropriate
110         int n = _predicates.size();
111         for (int i = 0; i < n; i++) {
112             Predicate pred = (Predicate) _predicates.get(i);
113 
114             if (!canOptimize) {
115                 pred.dontOptimize();
116             }
117             pred.typeCheck(stable);
118         }
119         return _type = Type.NodeSet;
120     }
121 
122     /**
123      * Translate a filter expression by pushing the appropriate iterator
124      * onto the stack.
125      */
translate(ClassGenerator classGen, MethodGenerator methodGen)126     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
127         translateFilterExpr(classGen, methodGen, _predicates == null ? -1 : _predicates.size() - 1);
128     }
129 
translateFilterExpr(ClassGenerator classGen, MethodGenerator methodGen, int predicateIndex)130     private void translateFilterExpr(ClassGenerator classGen,
131                                      MethodGenerator methodGen,
132                                      int predicateIndex) {
133         if (predicateIndex >= 0) {
134             translatePredicates(classGen, methodGen, predicateIndex);
135         }
136         else {
137             _primary.translate(classGen, methodGen);
138         }
139     }
140 
141     /**
142      * Translate a sequence of predicates. Each predicate is translated
143      * by constructing an instance of <code>CurrentNodeListIterator</code>
144      * which is initialized from another iterator (recursive call), a
145      * filter and a closure (call to translate on the predicate) and "this".
146      */
translatePredicates(ClassGenerator classGen, MethodGenerator methodGen, int predicateIndex)147     public void translatePredicates(ClassGenerator classGen,
148                                     MethodGenerator methodGen,
149                                     int predicateIndex) {
150         final ConstantPoolGen cpg = classGen.getConstantPool();
151         final InstructionList il = methodGen.getInstructionList();
152 
153         // If not predicates left, translate primary expression
154         if (predicateIndex < 0) {
155             translateFilterExpr(classGen, methodGen, predicateIndex);
156         }
157         else {
158             // Get the next predicate to be translated
159             Predicate predicate = (Predicate) _predicates.get(predicateIndex--);
160 
161             // Translate the rest of the predicates from right to left
162             translatePredicates(classGen, methodGen, predicateIndex);
163 
164             if (predicate.isNthPositionFilter()) {
165                 int nthIteratorIdx = cpg.addMethodref(NTH_ITERATOR_CLASS,
166                                        "<init>",
167                                        "("+NODE_ITERATOR_SIG+"I)V");
168 
169                 // Backwards branches are prohibited if an uninitialized object
170                 // is on the stack by section 4.9.4 of the JVM Specification,
171                 // 2nd Ed.  We don't know whether this code might contain
172                 // backwards branches, so we mustn't create the new object unti
173 
174                 // after we've created the suspect arguments to its constructor
175 
176                 // Instead we calculate the values of the arguments to the
177                 // constructor first, store them in temporary variables, create
178                 // the object and reload the arguments from the temporaries to
179                 // avoid the problem.
180                 LocalVariableGen iteratorTemp
181                         = methodGen.addLocalVariable("filter_expr_tmp1",
182                                          Util.getJCRefType(NODE_ITERATOR_SIG),
183                                          null, null);
184                 iteratorTemp.setStart(
185                         il.append(new ASTORE(iteratorTemp.getIndex())));
186 
187                 predicate.translate(classGen, methodGen);
188                 LocalVariableGen predicateValueTemp
189                         = methodGen.addLocalVariable("filter_expr_tmp2",
190                                          Util.getJCRefType("I"),
191                                          null, null);
192                 predicateValueTemp.setStart(
193                         il.append(new ISTORE(predicateValueTemp.getIndex())));
194 
195                 il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS)));
196                 il.append(DUP);
197                 iteratorTemp.setEnd(
198                         il.append(new ALOAD(iteratorTemp.getIndex())));
199                 predicateValueTemp.setEnd(
200                         il.append(new ILOAD(predicateValueTemp.getIndex())));
201                 il.append(new INVOKESPECIAL(nthIteratorIdx));
202             } else {
203                     // Translate predicates from right to left
204                 final int initCNLI = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
205                                                       "<init>",
206                                                       "("+NODE_ITERATOR_SIG+"Z"+
207                                                       CURRENT_NODE_LIST_FILTER_SIG +
208                                                       NODE_SIG+TRANSLET_SIG+")V");
209 
210                 // Backwards branches are prohibited if an uninitialized object is
211                 // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
212                 // We don't know whether this code might contain backwards branches,
213                 // so we mustn't create the new object until after we've created
214                 // the suspect arguments to its constructor.  Instead we calculate
215                 // the values of the arguments to the constructor first, store them
216                 // in temporary variables, create the object and reload the
217                 // arguments from the temporaries to avoid the problem.
218 
219 
220                 LocalVariableGen nodeIteratorTemp =
221                     methodGen.addLocalVariable("filter_expr_tmp1",
222                                                Util.getJCRefType(NODE_ITERATOR_SIG),
223                                                null, null);
224                 nodeIteratorTemp.setStart(
225                         il.append(new ASTORE(nodeIteratorTemp.getIndex())));
226 
227                 predicate.translate(classGen, methodGen);
228                 LocalVariableGen filterTemp =
229                     methodGen.addLocalVariable("filter_expr_tmp2",
230                                   Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG),
231                                   null, null);
232                 filterTemp.setStart(il.append(new ASTORE(filterTemp.getIndex())));
233 
234                 // Create a CurrentNodeListIterator
235                 il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR)));
236                 il.append(DUP);
237 
238                 // Initialize CurrentNodeListIterator
239                 nodeIteratorTemp.setEnd(
240                         il.append(new ALOAD(nodeIteratorTemp.getIndex())));
241                 il.append(ICONST_1);
242                 filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex())));
243                 il.append(methodGen.loadCurrentNode());
244                 il.append(classGen.loadTranslet());
245                 il.append(new INVOKESPECIAL(initCNLI));
246             }
247         }
248     }
249 }
250