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