1 /*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * @LastModified: Oct 2017
4 */
5 /*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements. See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24 import com.sun.org.apache.bcel.internal.classfile.Field;
25 import com.sun.org.apache.bcel.internal.generic.ASTORE;
26 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
27 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
28 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
29 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
30 import com.sun.org.apache.bcel.internal.generic.InstructionList;
31 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
32 import com.sun.org.apache.bcel.internal.generic.NEW;
33 import com.sun.org.apache.bcel.internal.generic.PUSH;
34 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.FilterGenerator;
38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TestGenerator;
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
47 import com.sun.org.apache.xalan.internal.xsltc.runtime.Operators;
48 import java.util.ArrayList;
49 import java.util.List;
50
51 /**
52 * @author Jacek Ambroziak
53 * @author Santiago Pericas-Geertsen
54 * @author Morten Jorgensen
55 */
56 final class Predicate extends Expression implements Closure {
57
58 /**
59 * The predicate's expression.
60 */
61 private Expression _exp = null;
62
63 /**
64 * This flag indicates if optimizations are turned on. The
65 * method <code>dontOptimize()</code> can be called to turn
66 * optimizations off.
67 */
68 private boolean _canOptimize = true;
69
70 /**
71 * Flag indicatig if the nth position optimization is on. It
72 * is set in <code>typeCheck()</code>.
73 */
74 private boolean _nthPositionFilter = false;
75
76 /**
77 * Flag indicatig if the nth position descendant is on. It
78 * is set in <code>typeCheck()</code>.
79 */
80 private boolean _nthDescendant = false;
81
82 /**
83 * Cached node type of the expression that owns this predicate.
84 */
85 int _ptype = -1;
86
87 /**
88 * Name of the inner class.
89 */
90 private String _className = null;
91
92 /**
93 * List of variables in closure.
94 */
95 private List<VariableRefBase> _closureVars = null;
96
97 /**
98 * Reference to parent closure.
99 */
100 private Closure _parentClosure = null;
101
102 /**
103 * Cached value of method <code>getCompareValue()</code>.
104 */
105 private Expression _value = null;
106
107 /**
108 * Cached value of method <code>getCompareValue()</code>.
109 */
110 private Step _step = null;
111
112 /**
113 * Initializes a predicate.
114 */
115 public Predicate(Expression exp) {
116 _exp = exp;
117 _exp.setParent(this);
118
119 }
120
121 /**
122 * Set the parser for this expression.
123 */
124 public void setParser(Parser parser) {
125 super.setParser(parser);
126 _exp.setParser(parser);
127 }
128
129 /**
130 * Returns a boolean value indicating if the nth position optimization
131 * is on. Must be call after type checking!
132 */
133 public boolean isNthPositionFilter() {
134 return _nthPositionFilter;
135 }
136
137 /**
138 * Returns a boolean value indicating if the nth descendant optimization
139 * is on. Must be call after type checking!
140 */
141 public boolean isNthDescendant() {
142 return _nthDescendant;
143 }
144
145 /**
146 * Turns off all optimizations for this predicate.
147 */
148 public void dontOptimize() {
149 _canOptimize = false;
150 }
151
152 /**
153 * Returns true if the expression in this predicate contains a call
154 * to position().
155 */
156 public boolean hasPositionCall() {
157 return _exp.hasPositionCall();
158 }
159
160 /**
161 * Returns true if the expression in this predicate contains a call
162 * to last().
163 */
164 public boolean hasLastCall() {
165 return _exp.hasLastCall();
166 }
167
168 // -- Begin Closure interface --------------------
169
170 /**
171 * Returns true if this closure is compiled in an inner class (i.e.
172 * if this is a real closure).
173 */
174 public boolean inInnerClass() {
175 return (_className != null);
176 }
177
178 /**
179 * Returns a reference to its parent closure or null if outermost.
180 */
181 public Closure getParentClosure() {
182 if (_parentClosure == null) {
183 SyntaxTreeNode node = getParent();
184 do {
185 if (node instanceof Closure) {
186 _parentClosure = (Closure) node;
187 break;
188 }
189 if (node instanceof TopLevelElement) {
190 break; // way up in the tree
191 }
192 node = node.getParent();
193 } while (node != null);
194 }
195 return _parentClosure;
196 }
197
198 /**
199 * Returns the name of the auxiliary class or null if this predicate
200 * is compiled inside the Translet.
201 */
202 public String getInnerClassName() {
203 return _className;
204 }
205
206 /**
207 * Add new variable to the closure.
208 */
209 public void addVariable(VariableRefBase variableRef) {
210 if (_closureVars == null) {
211 _closureVars = new ArrayList<>();
212 }
213
214 // Only one reference per variable
215 if (!_closureVars.contains(variableRef)) {
216 _closureVars.add(variableRef);
217
218 // Add variable to parent closure as well
219 Closure parentClosure = getParentClosure();
220 if (parentClosure != null) {
221 parentClosure.addVariable(variableRef);
222 }
223 }
224 }
225
226 // -- End Closure interface ----------------------
227
228 /**
229 * Returns the node type of the expression owning this predicate. The
230 * return value is cached in <code>_ptype</code>.
231 */
232 public int getPosType() {
233 if (_ptype == -1) {
234 SyntaxTreeNode parent = getParent();
235 if (parent instanceof StepPattern) {
236 _ptype = ((StepPattern)parent).getNodeType();
237 }
238 else if (parent instanceof AbsoluteLocationPath) {
239 AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
240 Expression exp = path.getPath();
241 if (exp instanceof Step) {
242 _ptype = ((Step)exp).getNodeType();
243 }
244 }
245 else if (parent instanceof VariableRefBase) {
246 final VariableRefBase ref = (VariableRefBase)parent;
247 final VariableBase var = ref.getVariable();
248 final Expression exp = var.getExpression();
249 if (exp instanceof Step) {
250 _ptype = ((Step)exp).getNodeType();
251 }
252 }
253 else if (parent instanceof Step) {
254 _ptype = ((Step)parent).getNodeType();
255 }
256 }
257 return _ptype;
258 }
259
260 public boolean parentIsPattern() {
261 return (getParent() instanceof Pattern);
262 }
263
264 public Expression getExpr() {
265 return _exp;
266 }
267
268 public String toString() {
269 return "pred(" + _exp + ')';
270 }
271
272 /**
273 * Type check a predicate expression. If the type of the expression is
274 * number convert it to boolean by adding a comparison with position().
275 * Note that if the expression is a parameter, we cannot distinguish
276 * at compile time if its type is number or not. Hence, expressions of
277 * reference type are always converted to booleans.
278 *
279 * This method may be called twice, before and after calling
280 * <code>dontOptimize()</code>. If so, the second time it should honor
281 * the new value of <code>_canOptimize</code>.
282 */
283 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
284 Type texp = _exp.typeCheck(stable);
285
286 // We need explicit type information for reference types - no good!
287 if (texp instanceof ReferenceType) {
288 _exp = new CastExpr(_exp, texp = Type.Real);
289 }
290
291 // A result tree fragment should not be cast directly to a number type,
292 // but rather to a boolean value, and then to a numer (0 or 1).
293 // Ref. section 11.2 of the XSLT 1.0 spec
294 if (texp instanceof ResultTreeType) {
295 _exp = new CastExpr(_exp, Type.Boolean);
296 _exp = new CastExpr(_exp, Type.Real);
297 texp = _exp.typeCheck(stable);
298 }
299
300 // Numerical types will be converted to a position filter
301 if (texp instanceof NumberType) {
302 // Cast any numerical types to an integer
303 if (texp instanceof IntType == false) {
304 _exp = new CastExpr(_exp, Type.Int);
305 }
306
307 if (_canOptimize) {
308 // Nth position optimization. Expression must not depend on context
309 _nthPositionFilter =
310 !_exp.hasLastCall() && !_exp.hasPositionCall();
311
312 // _nthDescendant optimization - only if _nthPositionFilter is on
313 if (_nthPositionFilter) {
314 SyntaxTreeNode parent = getParent();
315 _nthDescendant = (parent instanceof Step) &&
316 (parent.getParent() instanceof AbsoluteLocationPath);
317 return _type = Type.NodeSet;
318 }
319 }
320
321 // Reset optimization flags
322 _nthPositionFilter = _nthDescendant = false;
323
324 // Otherwise, expand [e] to [position() = e]
325 final QName position =
326 getParser().getQNameIgnoreDefaultNs("position");
327 final PositionCall positionCall =
328 new PositionCall(position);
329 positionCall.setParser(getParser());
330 positionCall.setParent(this);
331
332 _exp = new EqualityExpr(Operators.EQ, positionCall,
333 _exp);
334 if (_exp.typeCheck(stable) != Type.Boolean) {
335 _exp = new CastExpr(_exp, Type.Boolean);
336 }
337 return _type = Type.Boolean;
338 }
339 else {
340 // All other types will be handled as boolean values
341 if (texp instanceof BooleanType == false) {
342 _exp = new CastExpr(_exp, Type.Boolean);
343 }
344 return _type = Type.Boolean;
345 }
346 }
347
348 /**
349 * Create a new "Filter" class implementing
350 * <code>CurrentNodeListFilter</code>. Allocate registers for local
351 * variables and local parameters passed in the closure to test().
352 * Notice that local variables need to be "unboxed".
353 */
354 private void compileFilter(ClassGenerator classGen,
355 MethodGenerator methodGen) {
356 TestGenerator testGen;
357 LocalVariableGen local;
358 FilterGenerator filterGen;
359
360 _className = getXSLTC().getHelperClassName();
361 filterGen = new FilterGenerator(_className,
362 "java.lang.Object",
363 toString(),
364 ACC_PUBLIC | ACC_SUPER,
365 new String[] {
366 CURRENT_NODE_LIST_FILTER
367 },
368 classGen.getStylesheet());
369
370 final ConstantPoolGen cpg = filterGen.getConstantPool();
371 final int length = (_closureVars == null) ? 0 : _closureVars.size();
372
373 // Add a new instance variable for each var in closure
374 for (int i = 0; i < length; i++) {
375 VariableBase var = (_closureVars.get(i)).getVariable();
376
377 filterGen.addField(new Field(ACC_PUBLIC,
378 cpg.addUtf8(var.getEscapedName()),
379 cpg.addUtf8(var.getType().toSignature()),
380 null, cpg.getConstantPool()));
381 }
382
383 final InstructionList il = new InstructionList();
384 testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
385 com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
386 new com.sun.org.apache.bcel.internal.generic.Type[] {
387 com.sun.org.apache.bcel.internal.generic.Type.INT,
388 com.sun.org.apache.bcel.internal.generic.Type.INT,
389 com.sun.org.apache.bcel.internal.generic.Type.INT,
390 com.sun.org.apache.bcel.internal.generic.Type.INT,
391 Util.getJCRefType(TRANSLET_SIG),
392 Util.getJCRefType(NODE_ITERATOR_SIG)
393 },
394 new String[] {
395 "node",
396 "position",
397 "last",
398 "current",
399 "translet",
400 "iterator"
401 },
402 "test", _className, il, cpg);
403
404 // Store the dom in a local variable
405 local = testGen.addLocalVariable("document",
406 Util.getJCRefType(DOM_INTF_SIG),
407 null, null);
408 final String className = classGen.getClassName();
409 il.append(filterGen.loadTranslet());
410 il.append(new CHECKCAST(cpg.addClass(className)));
411 il.append(new GETFIELD(cpg.addFieldref(className,
412 DOM_FIELD, DOM_INTF_SIG)));
413 local.setStart(il.append(new ASTORE(local.getIndex())));
414
415 // Store the dom index in the test generator
416 testGen.setDomIndex(local.getIndex());
417
418 _exp.translate(filterGen, testGen);
419 il.append(IRETURN);
420
421 filterGen.addEmptyConstructor(ACC_PUBLIC);
422 filterGen.addMethod(testGen);
423
424 getXSLTC().dumpClass(filterGen.getJavaClass());
425 }
426
427 /**
428 * Returns true if the predicate is a test for the existance of an
429 * element or attribute. All we have to do is to get the first node
430 * from the step, check if it is there, and then return true/false.
431 */
432 public boolean isBooleanTest() {
433 return (_exp instanceof BooleanExpr);
434 }
435
436 /**
437 * Method to see if we can optimise the predicate by using a specialised
438 * iterator for expressions like '/foo/bar[@attr = $var]', which are
439 * very common in many stylesheets
440 */
441 public boolean isNodeValueTest() {
442 if (!_canOptimize) return false;
443 return (getStep() != null && getCompareValue() != null);
444 }
445
446 /**
447 * Returns the step in an expression of the form 'step = value'.
448 * Null is returned if the expression is not of the right form.
449 * Optimization if off if null is returned.
450 */
451 public Step getStep() {
452 // Returned cached value if called more than once
453 if (_step != null) {
454 return _step;
455 }
456
457 // Nothing to do if _exp is null
458 if (_exp == null) {
459 return null;
460 }
461
462 // Ignore if not equality expression
463 if (_exp instanceof EqualityExpr) {
464 EqualityExpr exp = (EqualityExpr)_exp;
465 Expression left = exp.getLeft();
466 Expression right = exp.getRight();
467
468 // Unwrap and set _step if appropriate
469 if (left instanceof CastExpr) {
470 left = ((CastExpr) left).getExpr();
471 }
472 if (left instanceof Step) {
473 _step = (Step) left;
474 }
475
476 // Unwrap and set _step if appropriate
477 if (right instanceof CastExpr) {
478 right = ((CastExpr)right).getExpr();
479 }
480 if (right instanceof Step) {
481 _step = (Step)right;
482 }
483 }
484 return _step;
485 }
486
487 /**
488 * Returns the value in an expression of the form 'step = value'.
489 * A value may be either a literal string or a variable whose
490 * type is string. Optimization if off if null is returned.
491 */
492 public Expression getCompareValue() {
493 // Returned cached value if called more than once
494 if (_value != null) {
495 return _value;
496 }
497
498 // Nothing to to do if _exp is null
499 if (_exp == null) {
500 return null;
501 }
502
503 // Ignore if not an equality expression
504 if (_exp instanceof EqualityExpr) {
505 EqualityExpr exp = (EqualityExpr) _exp;
506 Expression left = exp.getLeft();
507 Expression right = exp.getRight();
508
509 // Return if left is literal string
510 if (left instanceof LiteralExpr) {
511 _value = left;
512 return _value;
513 }
514 // Return if left is a variable reference of type string
515 if (left instanceof VariableRefBase &&
516 left.getType() == Type.String)
517 {
518 _value = left;
519 return _value;
520 }
521
522 // Return if right is literal string
523 if (right instanceof LiteralExpr) {
524 _value = right;
525 return _value;
526 }
527 // Return if left is a variable reference whose type is string
528 if (right instanceof VariableRefBase &&
529 right.getType() == Type.String)
530 {
531 _value = right;
532 return _value;
533 }
534 }
535 return null;
536 }
537
538 /**
539 * Translate a predicate expression. This translation pushes
540 * two references on the stack: a reference to a newly created
541 * filter object and a reference to the predicate's closure.
542 */
543 public void translateFilter(ClassGenerator classGen,
544 MethodGenerator methodGen)
545 {
546 final ConstantPoolGen cpg = classGen.getConstantPool();
547 final InstructionList il = methodGen.getInstructionList();
548
549 // Compile auxiliary class for filter
550 compileFilter(classGen, methodGen);
551
552 // Create new instance of filter
553 il.append(new NEW(cpg.addClass(_className)));
554 il.append(DUP);
555 il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
556 "<init>", "()V")));
557
558 // Initialize closure variables
559 final int length = (_closureVars == null) ? 0 : _closureVars.size();
560
561 for (int i = 0; i < length; i++) {
562 VariableRefBase varRef = _closureVars.get(i);
563 VariableBase var = varRef.getVariable();
564 Type varType = var.getType();
565
566 il.append(DUP);
567
568 // Find nearest closure implemented as an inner class
569 Closure variableClosure = _parentClosure;
570 while (variableClosure != null) {
571 if (variableClosure.inInnerClass()) break;
572 variableClosure = variableClosure.getParentClosure();
573 }
574
575 // Use getfield if in an inner class
576 if (variableClosure != null) {
577 il.append(ALOAD_0);
578 il.append(new GETFIELD(
579 cpg.addFieldref(variableClosure.getInnerClassName(),
580 var.getEscapedName(), varType.toSignature())));
581 }
582 else {
583 // Use a load of instruction if in translet class
584 il.append(var.loadInstruction());
585 }
586
587 // Store variable in new closure
588 il.append(new PUTFIELD(
589 cpg.addFieldref(_className, var.getEscapedName(),
590 varType.toSignature())));
591 }
592 }
593
594 /**
595 * Translate a predicate expression. If non of the optimizations apply
596 * then this translation pushes two references on the stack: a reference
597 * to a newly created filter object and a reference to the predicate's
598 * closure. See class <code>Step</code> for further details.
599 */
600 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
601
602 final ConstantPoolGen cpg = classGen.getConstantPool();
603 final InstructionList il = methodGen.getInstructionList();
604
605 if (_nthPositionFilter || _nthDescendant) {
606 _exp.translate(classGen, methodGen);
607 }
608 else if (isNodeValueTest() && (getParent() instanceof Step)) {
609 _value.translate(classGen, methodGen);
610 il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
611 il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
612 }
613 else {
614 translateFilter(classGen, methodGen);
615 }
616 }
617 }
--- EOF ---