| /* |
| * reserved comment block |
| * DO NOT REMOVE OR ALTER! |
| */ |
| /* |
| * Copyright 2001-2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id: StepPattern.java,v 1.2.4.1 2005/09/12 11:13:19 pvedula Exp $ |
| */ |
| |
| package com.sun.org.apache.xalan.internal.xsltc.compiler; |
| |
| import java.util.Vector; |
| |
| import com.sun.org.apache.bcel.internal.classfile.Field; |
| import com.sun.org.apache.bcel.internal.generic.ALOAD; |
| import com.sun.org.apache.bcel.internal.generic.ASTORE; |
| import com.sun.org.apache.bcel.internal.generic.BranchHandle; |
| import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; |
| import com.sun.org.apache.bcel.internal.generic.GETFIELD; |
| import com.sun.org.apache.bcel.internal.generic.GOTO; |
| import com.sun.org.apache.bcel.internal.generic.GOTO_W; |
| import com.sun.org.apache.bcel.internal.generic.IFLT; |
| import com.sun.org.apache.bcel.internal.generic.IFNE; |
| import com.sun.org.apache.bcel.internal.generic.IFNONNULL; |
| import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ; |
| import com.sun.org.apache.bcel.internal.generic.IF_ICMPLT; |
| import com.sun.org.apache.bcel.internal.generic.IF_ICMPNE; |
| import com.sun.org.apache.bcel.internal.generic.ILOAD; |
| import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; |
| import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; |
| import com.sun.org.apache.bcel.internal.generic.ISTORE; |
| import com.sun.org.apache.bcel.internal.generic.InstructionHandle; |
| import com.sun.org.apache.bcel.internal.generic.InstructionList; |
| import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; |
| import com.sun.org.apache.bcel.internal.generic.NEW; |
| import com.sun.org.apache.bcel.internal.generic.PUSH; |
| import com.sun.org.apache.bcel.internal.generic.PUTFIELD; |
| import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; |
| import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; |
| import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; |
| import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; |
| import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; |
| import com.sun.org.apache.xml.internal.dtm.Axis; |
| import com.sun.org.apache.xml.internal.dtm.DTM; |
| |
| /** |
| * @author Jacek Ambroziak |
| * @author Santiago Pericas-Geertsen |
| * @author Erwin Bolwidt <ejb@klomp.org> |
| */ |
| class StepPattern extends RelativePathPattern { |
| |
| private static final int NO_CONTEXT = 0; |
| private static final int SIMPLE_CONTEXT = 1; |
| private static final int GENERAL_CONTEXT = 2; |
| |
| protected final int _axis; |
| protected final int _nodeType; |
| protected Vector _predicates; |
| |
| private Step _step = null; |
| private boolean _isEpsilon = false; |
| private int _contextCase; |
| |
| private double _priority = Double.MAX_VALUE; |
| |
| public StepPattern(int axis, int nodeType, Vector predicates) { |
| _axis = axis; |
| _nodeType = nodeType; |
| _predicates = predicates; |
| } |
| |
| public void setParser(Parser parser) { |
| super.setParser(parser); |
| if (_predicates != null) { |
| final int n = _predicates.size(); |
| for (int i = 0; i < n; i++) { |
| final Predicate exp = (Predicate)_predicates.elementAt(i); |
| exp.setParser(parser); |
| exp.setParent(this); |
| } |
| } |
| } |
| |
| public int getNodeType() { |
| return _nodeType; |
| } |
| |
| public void setPriority(double priority) { |
| _priority = priority; |
| } |
| |
| public StepPattern getKernelPattern() { |
| return this; |
| } |
| |
| public boolean isWildcard() { |
| return _isEpsilon && hasPredicates() == false; |
| } |
| |
| public StepPattern setPredicates(Vector predicates) { |
| _predicates = predicates; |
| return(this); |
| } |
| |
| protected boolean hasPredicates() { |
| return _predicates != null && _predicates.size() > 0; |
| } |
| |
| public double getDefaultPriority() { |
| if (_priority != Double.MAX_VALUE) { |
| return _priority; |
| } |
| |
| if (hasPredicates()) { |
| return 0.5; |
| } |
| else { |
| switch(_nodeType) { |
| case -1: |
| return -0.5; // node() |
| case 0: |
| return 0.0; |
| default: |
| return (_nodeType >= NodeTest.GTYPE) ? 0.0 : -0.5; |
| } |
| } |
| } |
| |
| public int getAxis() { |
| return _axis; |
| } |
| |
| public void reduceKernelPattern() { |
| _isEpsilon = true; |
| } |
| |
| public String toString() { |
| final StringBuffer buffer = new StringBuffer("stepPattern(\""); |
| buffer.append(Axis.getNames(_axis)) |
| .append("\", ") |
| .append(_isEpsilon ? |
| ("epsilon{" + Integer.toString(_nodeType) + "}") : |
| Integer.toString(_nodeType)); |
| if (_predicates != null) |
| buffer.append(", ").append(_predicates.toString()); |
| return buffer.append(')').toString(); |
| } |
| |
| private int analyzeCases() { |
| boolean noContext = true; |
| final int n = _predicates.size(); |
| |
| for (int i = 0; i < n && noContext; i++) { |
| Predicate pred = (Predicate) _predicates.elementAt(i); |
| if (pred.isNthPositionFilter() || |
| pred.hasPositionCall() || |
| pred.hasLastCall()) |
| { |
| noContext = false; |
| } |
| } |
| |
| if (noContext) { |
| return NO_CONTEXT; |
| } |
| else if (n == 1) { |
| return SIMPLE_CONTEXT; |
| } |
| return GENERAL_CONTEXT; |
| } |
| |
| private String getNextFieldName() { |
| return "__step_pattern_iter_" + getXSLTC().nextStepPatternSerial(); |
| } |
| |
| public Type typeCheck(SymbolTable stable) throws TypeCheckError { |
| if (hasPredicates()) { |
| // Type check all the predicates (e -> position() = e) |
| final int n = _predicates.size(); |
| for (int i = 0; i < n; i++) { |
| final Predicate pred = (Predicate)_predicates.elementAt(i); |
| pred.typeCheck(stable); |
| } |
| |
| // Analyze context cases |
| _contextCase = analyzeCases(); |
| |
| Step step = null; |
| |
| // Create an instance of Step to do the translation |
| if (_contextCase == SIMPLE_CONTEXT) { |
| Predicate pred = (Predicate)_predicates.elementAt(0); |
| if (pred.isNthPositionFilter()) { |
| _contextCase = GENERAL_CONTEXT; |
| step = new Step(_axis, _nodeType, _predicates); |
| } else { |
| step = new Step(_axis, _nodeType, null); |
| } |
| } else if (_contextCase == GENERAL_CONTEXT) { |
| final int len = _predicates.size(); |
| for (int i = 0; i < len; i++) { |
| ((Predicate)_predicates.elementAt(i)).dontOptimize(); |
| } |
| |
| step = new Step(_axis, _nodeType, _predicates); |
| } |
| |
| if (step != null) { |
| step.setParser(getParser()); |
| step.typeCheck(stable); |
| _step = step; |
| } |
| } |
| return _axis == Axis.CHILD ? Type.Element : Type.Attribute; |
| } |
| |
| private void translateKernel(ClassGenerator classGen, |
| MethodGenerator methodGen) { |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| if (_nodeType == DTM.ELEMENT_NODE) { |
| final int check = cpg.addInterfaceMethodref(DOM_INTF, |
| "isElement", "(I)Z"); |
| il.append(methodGen.loadDOM()); |
| il.append(SWAP); |
| il.append(new INVOKEINTERFACE(check, 2)); |
| |
| // Need to allow for long jumps here |
| final BranchHandle icmp = il.append(new IFNE(null)); |
| _falseList.add(il.append(new GOTO_W(null))); |
| icmp.setTarget(il.append(NOP)); |
| } |
| else if (_nodeType == DTM.ATTRIBUTE_NODE) { |
| final int check = cpg.addInterfaceMethodref(DOM_INTF, |
| "isAttribute", "(I)Z"); |
| il.append(methodGen.loadDOM()); |
| il.append(SWAP); |
| il.append(new INVOKEINTERFACE(check, 2)); |
| |
| // Need to allow for long jumps here |
| final BranchHandle icmp = il.append(new IFNE(null)); |
| _falseList.add(il.append(new GOTO_W(null))); |
| icmp.setTarget(il.append(NOP)); |
| } |
| else { |
| // context node is on the stack |
| final int getEType = cpg.addInterfaceMethodref(DOM_INTF, |
| "getExpandedTypeID", |
| "(I)I"); |
| il.append(methodGen.loadDOM()); |
| il.append(SWAP); |
| il.append(new INVOKEINTERFACE(getEType, 2)); |
| il.append(new PUSH(cpg, _nodeType)); |
| |
| // Need to allow for long jumps here |
| final BranchHandle icmp = il.append(new IF_ICMPEQ(null)); |
| _falseList.add(il.append(new GOTO_W(null))); |
| icmp.setTarget(il.append(NOP)); |
| } |
| } |
| |
| private void translateNoContext(ClassGenerator classGen, |
| MethodGenerator methodGen) { |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| // Push current node on the stack |
| il.append(methodGen.loadCurrentNode()); |
| il.append(SWAP); |
| |
| // Overwrite current node with matching node |
| il.append(methodGen.storeCurrentNode()); |
| |
| // If pattern not reduced then check kernel |
| if (!_isEpsilon) { |
| il.append(methodGen.loadCurrentNode()); |
| translateKernel(classGen, methodGen); |
| } |
| |
| // Compile the expressions within the predicates |
| final int n = _predicates.size(); |
| for (int i = 0; i < n; i++) { |
| Predicate pred = (Predicate)_predicates.elementAt(i); |
| Expression exp = pred.getExpr(); |
| exp.translateDesynthesized(classGen, methodGen); |
| _trueList.append(exp._trueList); |
| _falseList.append(exp._falseList); |
| } |
| |
| // Backpatch true list and restore current iterator/node |
| InstructionHandle restore; |
| restore = il.append(methodGen.storeCurrentNode()); |
| backPatchTrueList(restore); |
| BranchHandle skipFalse = il.append(new GOTO(null)); |
| |
| // Backpatch false list and restore current iterator/node |
| restore = il.append(methodGen.storeCurrentNode()); |
| backPatchFalseList(restore); |
| _falseList.add(il.append(new GOTO(null))); |
| |
| // True list falls through |
| skipFalse.setTarget(il.append(NOP)); |
| } |
| |
| private void translateSimpleContext(ClassGenerator classGen, |
| MethodGenerator methodGen) { |
| int index; |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| // Store matching node into a local variable |
| LocalVariableGen match; |
| match = methodGen.addLocalVariable("step_pattern_tmp1", |
| Util.getJCRefType(NODE_SIG), |
| null, null); |
| match.setStart(il.append(new ISTORE(match.getIndex()))); |
| |
| // If pattern not reduced then check kernel |
| if (!_isEpsilon) { |
| il.append(new ILOAD(match.getIndex())); |
| translateKernel(classGen, methodGen); |
| } |
| |
| // Push current iterator and current node on the stack |
| il.append(methodGen.loadCurrentNode()); |
| il.append(methodGen.loadIterator()); |
| |
| // Create a new matching iterator using the matching node |
| index = cpg.addMethodref(MATCHING_ITERATOR, "<init>", |
| "(I" + NODE_ITERATOR_SIG + ")V"); |
| |
| // Backwards branches are prohibited if an uninitialized object is |
| // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed. |
| // We don't know whether this code might contain backwards branches, |
| // so we mustn't create the new object until after we've created |
| // the suspect arguments to its constructor. Instead we calculate |
| // the values of the arguments to the constructor first, store them |
| // in temporary variables, create the object and reload the |
| // arguments from the temporaries to avoid the problem. |
| |
| _step.translate(classGen, methodGen); |
| LocalVariableGen stepIteratorTemp = |
| methodGen.addLocalVariable("step_pattern_tmp2", |
| Util.getJCRefType(NODE_ITERATOR_SIG), |
| null, null); |
| stepIteratorTemp.setStart( |
| il.append(new ASTORE(stepIteratorTemp.getIndex()))); |
| |
| il.append(new NEW(cpg.addClass(MATCHING_ITERATOR))); |
| il.append(DUP); |
| il.append(new ILOAD(match.getIndex())); |
| stepIteratorTemp.setEnd( |
| il.append(new ALOAD(stepIteratorTemp.getIndex()))); |
| il.append(new INVOKESPECIAL(index)); |
| |
| // Get the parent of the matching node |
| il.append(methodGen.loadDOM()); |
| il.append(new ILOAD(match.getIndex())); |
| index = cpg.addInterfaceMethodref(DOM_INTF, GET_PARENT, GET_PARENT_SIG); |
| il.append(new INVOKEINTERFACE(index, 2)); |
| |
| // Start the iterator with the parent |
| il.append(methodGen.setStartNode()); |
| |
| // Overwrite current iterator and current node |
| il.append(methodGen.storeIterator()); |
| match.setEnd(il.append(new ILOAD(match.getIndex()))); |
| il.append(methodGen.storeCurrentNode()); |
| |
| // Translate the expression of the predicate |
| Predicate pred = (Predicate) _predicates.elementAt(0); |
| Expression exp = pred.getExpr(); |
| exp.translateDesynthesized(classGen, methodGen); |
| |
| // Backpatch true list and restore current iterator/node |
| InstructionHandle restore = il.append(methodGen.storeIterator()); |
| il.append(methodGen.storeCurrentNode()); |
| exp.backPatchTrueList(restore); |
| BranchHandle skipFalse = il.append(new GOTO(null)); |
| |
| // Backpatch false list and restore current iterator/node |
| restore = il.append(methodGen.storeIterator()); |
| il.append(methodGen.storeCurrentNode()); |
| exp.backPatchFalseList(restore); |
| _falseList.add(il.append(new GOTO(null))); |
| |
| // True list falls through |
| skipFalse.setTarget(il.append(NOP)); |
| } |
| |
| private void translateGeneralContext(ClassGenerator classGen, |
| MethodGenerator methodGen) { |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| int iteratorIndex = 0; |
| BranchHandle ifBlock = null; |
| LocalVariableGen iter, node, node2; |
| final String iteratorName = getNextFieldName(); |
| |
| // Store node on the stack into a local variable |
| node = methodGen.addLocalVariable("step_pattern_tmp1", |
| Util.getJCRefType(NODE_SIG), |
| null, null); |
| node.setStart(il.append(new ISTORE(node.getIndex()))); |
| |
| // Create a new local to store the iterator |
| iter = methodGen.addLocalVariable("step_pattern_tmp2", |
| Util.getJCRefType(NODE_ITERATOR_SIG), |
| null, null); |
| |
| // Add a new private field if this is the main class |
| if (!classGen.isExternal()) { |
| final Field iterator = |
| new Field(ACC_PRIVATE, |
| cpg.addUtf8(iteratorName), |
| cpg.addUtf8(NODE_ITERATOR_SIG), |
| null, cpg.getConstantPool()); |
| classGen.addField(iterator); |
| iteratorIndex = cpg.addFieldref(classGen.getClassName(), |
| iteratorName, |
| NODE_ITERATOR_SIG); |
| |
| il.append(classGen.loadTranslet()); |
| il.append(new GETFIELD(iteratorIndex)); |
| il.append(DUP); |
| iter.setStart(il.append(new ASTORE(iter.getIndex()))); |
| ifBlock = il.append(new IFNONNULL(null)); |
| il.append(classGen.loadTranslet()); |
| } |
| |
| // Compile the step created at type checking time |
| _step.translate(classGen, methodGen); |
| InstructionHandle iterStore = il.append(new ASTORE(iter.getIndex())); |
| |
| // If in the main class update the field too |
| if (!classGen.isExternal()) { |
| il.append(new ALOAD(iter.getIndex())); |
| il.append(new PUTFIELD(iteratorIndex)); |
| ifBlock.setTarget(il.append(NOP)); |
| } else { |
| // If class is not external, start of range for iter variable was |
| // set above |
| iter.setStart(iterStore); |
| } |
| |
| // Get the parent of the node on the stack |
| il.append(methodGen.loadDOM()); |
| il.append(new ILOAD(node.getIndex())); |
| int index = cpg.addInterfaceMethodref(DOM_INTF, |
| GET_PARENT, GET_PARENT_SIG); |
| il.append(new INVOKEINTERFACE(index, 2)); |
| |
| // Initialize the iterator with the parent |
| il.append(new ALOAD(iter.getIndex())); |
| il.append(SWAP); |
| il.append(methodGen.setStartNode()); |
| |
| /* |
| * Inline loop: |
| * |
| * int node2; |
| * while ((node2 = iter.next()) != NodeIterator.END |
| * && node2 < node); |
| * return node2 == node; |
| */ |
| BranchHandle skipNext; |
| InstructionHandle begin, next; |
| node2 = methodGen.addLocalVariable("step_pattern_tmp3", |
| Util.getJCRefType(NODE_SIG), |
| null, null); |
| |
| skipNext = il.append(new GOTO(null)); |
| next = il.append(new ALOAD(iter.getIndex())); |
| node2.setStart(next); |
| begin = il.append(methodGen.nextNode()); |
| il.append(DUP); |
| il.append(new ISTORE(node2.getIndex())); |
| _falseList.add(il.append(new IFLT(null))); // NodeIterator.END |
| |
| il.append(new ILOAD(node2.getIndex())); |
| il.append(new ILOAD(node.getIndex())); |
| iter.setEnd(il.append(new IF_ICMPLT(next))); |
| |
| node2.setEnd(il.append(new ILOAD(node2.getIndex()))); |
| node.setEnd(il.append(new ILOAD(node.getIndex()))); |
| _falseList.add(il.append(new IF_ICMPNE(null))); |
| |
| skipNext.setTarget(begin); |
| } |
| |
| public void translate(ClassGenerator classGen, MethodGenerator methodGen) { |
| final ConstantPoolGen cpg = classGen.getConstantPool(); |
| final InstructionList il = methodGen.getInstructionList(); |
| |
| if (hasPredicates()) { |
| switch (_contextCase) { |
| case NO_CONTEXT: |
| translateNoContext(classGen, methodGen); |
| break; |
| |
| case SIMPLE_CONTEXT: |
| translateSimpleContext(classGen, methodGen); |
| break; |
| |
| default: |
| translateGeneralContext(classGen, methodGen); |
| break; |
| } |
| } |
| else if (isWildcard()) { |
| il.append(POP); // true list falls through |
| } |
| else { |
| translateKernel(classGen, methodGen); |
| } |
| } |
| } |