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: StepPattern.java,v 1.24 2004/02/16 22:24:29 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Vector;
  21. import com.sun.org.apache.bcel.internal.classfile.Field;
  22. import com.sun.org.apache.bcel.internal.generic.ALOAD;
  23. import com.sun.org.apache.bcel.internal.generic.ASTORE;
  24. import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  25. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  26. import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  27. import com.sun.org.apache.bcel.internal.generic.GOTO;
  28. import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  29. import com.sun.org.apache.bcel.internal.generic.IFLT;
  30. import com.sun.org.apache.bcel.internal.generic.IFNE;
  31. import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
  32. import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ;
  33. import com.sun.org.apache.bcel.internal.generic.IF_ICMPLT;
  34. import com.sun.org.apache.bcel.internal.generic.IF_ICMPNE;
  35. import com.sun.org.apache.bcel.internal.generic.ILOAD;
  36. import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  37. import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  38. import com.sun.org.apache.bcel.internal.generic.ISTORE;
  39. import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  40. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  41. import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  42. import com.sun.org.apache.bcel.internal.generic.NEW;
  43. import com.sun.org.apache.bcel.internal.generic.PUSH;
  44. import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  45. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  46. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  47. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  48. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  49. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  50. import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
  51. import com.sun.org.apache.xml.internal.dtm.DTM;
  52. /**
  53. * @author Jacek Ambroziak
  54. * @author Santiago Pericas-Geertsen
  55. * @author Erwin Bolwidt <ejb@klomp.org>
  56. */
  57. class StepPattern extends RelativePathPattern {
  58. private static final int NO_CONTEXT = 0;
  59. private static final int SIMPLE_CONTEXT = 1;
  60. private static final int GENERAL_CONTEXT = 2;
  61. protected final int _axis;
  62. protected final int _nodeType;
  63. protected Vector _predicates;
  64. private Step _step = null;
  65. private boolean _isEpsilon = false;
  66. private int _contextCase;
  67. private double _priority = Double.MAX_VALUE;
  68. public StepPattern(int axis, int nodeType, Vector predicates) {
  69. _axis = axis;
  70. _nodeType = nodeType;
  71. _predicates = predicates;
  72. }
  73. public void setParser(Parser parser) {
  74. super.setParser(parser);
  75. if (_predicates != null) {
  76. final int n = _predicates.size();
  77. for (int i = 0; i < n; i++) {
  78. final Predicate exp = (Predicate)_predicates.elementAt(i);
  79. exp.setParser(parser);
  80. exp.setParent(this);
  81. }
  82. }
  83. }
  84. public int getNodeType() {
  85. return _nodeType;
  86. }
  87. public void setPriority(double priority) {
  88. _priority = priority;
  89. }
  90. public StepPattern getKernelPattern() {
  91. return this;
  92. }
  93. public boolean isWildcard() {
  94. return _isEpsilon && hasPredicates() == false;
  95. }
  96. public StepPattern setPredicates(Vector predicates) {
  97. _predicates = predicates;
  98. return(this);
  99. }
  100. protected boolean hasPredicates() {
  101. return _predicates != null && _predicates.size() > 0;
  102. }
  103. public double getDefaultPriority() {
  104. if (_priority != Double.MAX_VALUE) {
  105. return _priority;
  106. }
  107. if (hasPredicates()) {
  108. return 0.5;
  109. }
  110. else {
  111. switch(_nodeType) {
  112. case -1:
  113. return -0.5; // node()
  114. case 0:
  115. return 0.0;
  116. default:
  117. return (_nodeType >= NodeTest.GTYPE) ? 0.0 : -0.5;
  118. }
  119. }
  120. }
  121. public int getAxis() {
  122. return _axis;
  123. }
  124. public void reduceKernelPattern() {
  125. _isEpsilon = true;
  126. }
  127. public String toString() {
  128. final StringBuffer buffer = new StringBuffer("stepPattern(\"");
  129. buffer.append(Axis.names[_axis])
  130. .append("\", ")
  131. .append(_isEpsilon ?
  132. ("epsilon{" + Integer.toString(_nodeType) + "}") :
  133. Integer.toString(_nodeType));
  134. if (_predicates != null)
  135. buffer.append(", ").append(_predicates.toString());
  136. return buffer.append(')').toString();
  137. }
  138. private int analyzeCases() {
  139. boolean noContext = true;
  140. final int n = _predicates.size();
  141. for (int i = 0; i < n && noContext; i++) {
  142. Predicate pred = (Predicate) _predicates.elementAt(i);
  143. if (pred.isNthPositionFilter() ||
  144. pred.hasPositionCall() ||
  145. pred.hasLastCall())
  146. {
  147. noContext = false;
  148. }
  149. }
  150. if (noContext) {
  151. return NO_CONTEXT;
  152. }
  153. else if (n == 1) {
  154. return SIMPLE_CONTEXT;
  155. }
  156. return GENERAL_CONTEXT;
  157. }
  158. private String getNextFieldName() {
  159. return "__step_pattern_iter_" + getXSLTC().nextStepPatternSerial();
  160. }
  161. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  162. if (hasPredicates()) {
  163. // Type check all the predicates (e -> position() = e)
  164. final int n = _predicates.size();
  165. for (int i = 0; i < n; i++) {
  166. final Predicate pred = (Predicate)_predicates.elementAt(i);
  167. pred.typeCheck(stable);
  168. }
  169. // Analyze context cases
  170. _contextCase = analyzeCases();
  171. Step step = null;
  172. // Create an instance of Step to do the translation
  173. if (_contextCase == SIMPLE_CONTEXT) {
  174. Predicate pred = (Predicate)_predicates.elementAt(0);
  175. if (pred.isNthPositionFilter()) {
  176. _contextCase = GENERAL_CONTEXT;
  177. step = new Step(_axis, _nodeType, _predicates);
  178. } else {
  179. step = new Step(_axis, _nodeType, null);
  180. }
  181. } else if (_contextCase == GENERAL_CONTEXT) {
  182. final int len = _predicates.size();
  183. for (int i = 0; i < len; i++) {
  184. ((Predicate)_predicates.elementAt(i)).dontOptimize();
  185. }
  186. step = new Step(_axis, _nodeType, _predicates);
  187. }
  188. if (step != null) {
  189. step.setParser(getParser());
  190. step.typeCheck(stable);
  191. _step = step;
  192. }
  193. }
  194. return _axis == Axis.CHILD ? Type.Element : Type.Attribute;
  195. }
  196. private void translateKernel(ClassGenerator classGen,
  197. MethodGenerator methodGen) {
  198. final ConstantPoolGen cpg = classGen.getConstantPool();
  199. final InstructionList il = methodGen.getInstructionList();
  200. if (_nodeType == DTM.ELEMENT_NODE) {
  201. final int check = cpg.addInterfaceMethodref(DOM_INTF,
  202. "isElement", "(I)Z");
  203. il.append(methodGen.loadDOM());
  204. il.append(SWAP);
  205. il.append(new INVOKEINTERFACE(check, 2));
  206. // Need to allow for long jumps here
  207. final BranchHandle icmp = il.append(new IFNE(null));
  208. _falseList.add(il.append(new GOTO_W(null)));
  209. icmp.setTarget(il.append(NOP));
  210. }
  211. else if (_nodeType == DTM.ATTRIBUTE_NODE) {
  212. final int check = cpg.addInterfaceMethodref(DOM_INTF,
  213. "isAttribute", "(I)Z");
  214. il.append(methodGen.loadDOM());
  215. il.append(SWAP);
  216. il.append(new INVOKEINTERFACE(check, 2));
  217. // Need to allow for long jumps here
  218. final BranchHandle icmp = il.append(new IFNE(null));
  219. _falseList.add(il.append(new GOTO_W(null)));
  220. icmp.setTarget(il.append(NOP));
  221. }
  222. else {
  223. // context node is on the stack
  224. final int getEType = cpg.addInterfaceMethodref(DOM_INTF,
  225. "getExpandedTypeID",
  226. "(I)I");
  227. il.append(methodGen.loadDOM());
  228. il.append(SWAP);
  229. il.append(new INVOKEINTERFACE(getEType, 2));
  230. il.append(new PUSH(cpg, _nodeType));
  231. // Need to allow for long jumps here
  232. final BranchHandle icmp = il.append(new IF_ICMPEQ(null));
  233. _falseList.add(il.append(new GOTO_W(null)));
  234. icmp.setTarget(il.append(NOP));
  235. }
  236. }
  237. private void translateNoContext(ClassGenerator classGen,
  238. MethodGenerator methodGen) {
  239. final ConstantPoolGen cpg = classGen.getConstantPool();
  240. final InstructionList il = methodGen.getInstructionList();
  241. // Push current node on the stack
  242. il.append(methodGen.loadCurrentNode());
  243. il.append(SWAP);
  244. // Overwrite current node with matching node
  245. il.append(methodGen.storeCurrentNode());
  246. // If pattern not reduced then check kernel
  247. if (!_isEpsilon) {
  248. il.append(methodGen.loadCurrentNode());
  249. translateKernel(classGen, methodGen);
  250. }
  251. // Compile the expressions within the predicates
  252. final int n = _predicates.size();
  253. for (int i = 0; i < n; i++) {
  254. Predicate pred = (Predicate)_predicates.elementAt(i);
  255. Expression exp = pred.getExpr();
  256. exp.translateDesynthesized(classGen, methodGen);
  257. _trueList.append(exp._trueList);
  258. _falseList.append(exp._falseList);
  259. }
  260. // Backpatch true list and restore current iterator/node
  261. InstructionHandle restore;
  262. restore = il.append(methodGen.storeCurrentNode());
  263. backPatchTrueList(restore);
  264. BranchHandle skipFalse = il.append(new GOTO(null));
  265. // Backpatch false list and restore current iterator/node
  266. restore = il.append(methodGen.storeCurrentNode());
  267. backPatchFalseList(restore);
  268. _falseList.add(il.append(new GOTO(null)));
  269. // True list falls through
  270. skipFalse.setTarget(il.append(NOP));
  271. }
  272. private void translateSimpleContext(ClassGenerator classGen,
  273. MethodGenerator methodGen) {
  274. int index;
  275. final ConstantPoolGen cpg = classGen.getConstantPool();
  276. final InstructionList il = methodGen.getInstructionList();
  277. // Store matching node into a local variable
  278. LocalVariableGen match;
  279. match = methodGen.addLocalVariable("step_pattern_tmp1",
  280. Util.getJCRefType(NODE_SIG),
  281. il.getEnd(), null);
  282. il.append(new ISTORE(match.getIndex()));
  283. // If pattern not reduced then check kernel
  284. if (!_isEpsilon) {
  285. il.append(new ILOAD(match.getIndex()));
  286. translateKernel(classGen, methodGen);
  287. }
  288. // Push current iterator and current node on the stack
  289. il.append(methodGen.loadCurrentNode());
  290. il.append(methodGen.loadIterator());
  291. // Create a new matching iterator using the matching node
  292. index = cpg.addMethodref(MATCHING_ITERATOR, "<init>",
  293. "(I" + NODE_ITERATOR_SIG + ")V");
  294. il.append(new NEW(cpg.addClass(MATCHING_ITERATOR)));
  295. il.append(DUP);
  296. il.append(new ILOAD(match.getIndex()));
  297. _step.translate(classGen, methodGen);
  298. il.append(new INVOKESPECIAL(index));
  299. // Get the parent of the matching node
  300. il.append(methodGen.loadDOM());
  301. il.append(new ILOAD(match.getIndex()));
  302. index = cpg.addInterfaceMethodref(DOM_INTF, GET_PARENT, GET_PARENT_SIG);
  303. il.append(new INVOKEINTERFACE(index, 2));
  304. // Start the iterator with the parent
  305. il.append(methodGen.setStartNode());
  306. // Overwrite current iterator and current node
  307. il.append(methodGen.storeIterator());
  308. il.append(new ILOAD(match.getIndex()));
  309. il.append(methodGen.storeCurrentNode());
  310. // Translate the expression of the predicate
  311. Predicate pred = (Predicate) _predicates.elementAt(0);
  312. Expression exp = pred.getExpr();
  313. exp.translateDesynthesized(classGen, methodGen);
  314. // Backpatch true list and restore current iterator/node
  315. InstructionHandle restore = il.append(methodGen.storeIterator());
  316. il.append(methodGen.storeCurrentNode());
  317. exp.backPatchTrueList(restore);
  318. BranchHandle skipFalse = il.append(new GOTO(null));
  319. // Backpatch false list and restore current iterator/node
  320. restore = il.append(methodGen.storeIterator());
  321. il.append(methodGen.storeCurrentNode());
  322. exp.backPatchFalseList(restore);
  323. _falseList.add(il.append(new GOTO(null)));
  324. // True list falls through
  325. skipFalse.setTarget(il.append(NOP));
  326. }
  327. private void translateGeneralContext(ClassGenerator classGen,
  328. MethodGenerator methodGen) {
  329. final ConstantPoolGen cpg = classGen.getConstantPool();
  330. final InstructionList il = methodGen.getInstructionList();
  331. int iteratorIndex = 0;
  332. BranchHandle ifBlock = null;
  333. LocalVariableGen iter, node, node2;
  334. final String iteratorName = getNextFieldName();
  335. // Store node on the stack into a local variable
  336. node = methodGen.addLocalVariable("step_pattern_tmp1",
  337. Util.getJCRefType(NODE_SIG),
  338. il.getEnd(), null);
  339. il.append(new ISTORE(node.getIndex()));
  340. // Create a new local to store the iterator
  341. iter = methodGen.addLocalVariable("step_pattern_tmp2",
  342. Util.getJCRefType(NODE_ITERATOR_SIG),
  343. il.getEnd(), null);
  344. // Add a new private field if this is the main class
  345. if (!classGen.isExternal()) {
  346. final Field iterator =
  347. new Field(ACC_PRIVATE,
  348. cpg.addUtf8(iteratorName),
  349. cpg.addUtf8(NODE_ITERATOR_SIG),
  350. null, cpg.getConstantPool());
  351. classGen.addField(iterator);
  352. iteratorIndex = cpg.addFieldref(classGen.getClassName(),
  353. iteratorName,
  354. NODE_ITERATOR_SIG);
  355. il.append(classGen.loadTranslet());
  356. il.append(new GETFIELD(iteratorIndex));
  357. il.append(DUP);
  358. il.append(new ASTORE(iter.getIndex()));
  359. ifBlock = il.append(new IFNONNULL(null));
  360. il.append(classGen.loadTranslet());
  361. }
  362. // Compile the step created at type checking time
  363. _step.translate(classGen, methodGen);
  364. il.append(new ASTORE(iter.getIndex()));
  365. // If in the main class update the field too
  366. if (!classGen.isExternal()) {
  367. il.append(new ALOAD(iter.getIndex()));
  368. il.append(new PUTFIELD(iteratorIndex));
  369. ifBlock.setTarget(il.append(NOP));
  370. }
  371. // Get the parent of the node on the stack
  372. il.append(methodGen.loadDOM());
  373. il.append(new ILOAD(node.getIndex()));
  374. int index = cpg.addInterfaceMethodref(DOM_INTF,
  375. GET_PARENT, GET_PARENT_SIG);
  376. il.append(new INVOKEINTERFACE(index, 2));
  377. // Initialize the iterator with the parent
  378. il.append(new ALOAD(iter.getIndex()));
  379. il.append(SWAP);
  380. il.append(methodGen.setStartNode());
  381. /*
  382. * Inline loop:
  383. *
  384. * int node2;
  385. * while ((node2 = iter.next()) != NodeIterator.END
  386. * && node2 < node);
  387. * return node2 == node;
  388. */
  389. BranchHandle skipNext;
  390. InstructionHandle begin, next;
  391. node2 = methodGen.addLocalVariable("step_pattern_tmp3",
  392. Util.getJCRefType(NODE_SIG),
  393. il.getEnd(), null);
  394. skipNext = il.append(new GOTO(null));
  395. next = il.append(new ALOAD(iter.getIndex()));
  396. begin = il.append(methodGen.nextNode());
  397. il.append(DUP);
  398. il.append(new ISTORE(node2.getIndex()));
  399. _falseList.add(il.append(new IFLT(null))); // NodeIterator.END
  400. il.append(new ILOAD(node2.getIndex()));
  401. il.append(new ILOAD(node.getIndex()));
  402. il.append(new IF_ICMPLT(next));
  403. il.append(new ILOAD(node2.getIndex()));
  404. il.append(new ILOAD(node.getIndex()));
  405. _falseList.add(il.append(new IF_ICMPNE(null)));
  406. skipNext.setTarget(begin);
  407. }
  408. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  409. final ConstantPoolGen cpg = classGen.getConstantPool();
  410. final InstructionList il = methodGen.getInstructionList();
  411. if (hasPredicates()) {
  412. switch (_contextCase) {
  413. case NO_CONTEXT:
  414. translateNoContext(classGen, methodGen);
  415. break;
  416. case SIMPLE_CONTEXT:
  417. translateSimpleContext(classGen, methodGen);
  418. break;
  419. default:
  420. translateGeneralContext(classGen, methodGen);
  421. break;
  422. }
  423. }
  424. else if (isWildcard()) {
  425. il.append(POP); // true list falls through
  426. }
  427. else {
  428. translateKernel(classGen, methodGen);
  429. }
  430. }
  431. }