--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Predicate.java Sun Aug 17 15:51:56 2014 +0100
@@ -0,0 +1,619 @@
+/*
+ * 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: Predicate.java,v 1.2.4.1 2005/09/12 11:02:18 pvedula Exp $
+ */
+
+package com.sun.org.apache.xalan.internal.xsltc.compiler;
+
+import java.util.ArrayList;
+
+import com.sun.org.apache.bcel.internal.classfile.Field;
+import com.sun.org.apache.bcel.internal.generic.ASTORE;
+import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
+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.INVOKESPECIAL;
+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.BooleanType;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.FilterGenerator;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TestGenerator;
+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.xalan.internal.xsltc.runtime.Operators;
+
+/**
+ * @author Jacek Ambroziak
+ * @author Santiago Pericas-Geertsen
+ * @author Morten Jorgensen
+ */
+final class Predicate extends Expression implements Closure {
+
+ /**
+ * The predicate's expression.
+ */
+ private Expression _exp = null;
+
+ /**
+ * This flag indicates if optimizations are turned on. The
+ * method <code>dontOptimize()</code> can be called to turn
+ * optimizations off.
+ */
+ private boolean _canOptimize = true;
+
+ /**
+ * Flag indicatig if the nth position optimization is on. It
+ * is set in <code>typeCheck()</code>.
+ */
+ private boolean _nthPositionFilter = false;
+
+ /**
+ * Flag indicatig if the nth position descendant is on. It
+ * is set in <code>typeCheck()</code>.
+ */
+ private boolean _nthDescendant = false;
+
+ /**
+ * Cached node type of the expression that owns this predicate.
+ */
+ int _ptype = -1;
+
+ /**
+ * Name of the inner class.
+ */
+ private String _className = null;
+
+ /**
+ * List of variables in closure.
+ */
+ private ArrayList _closureVars = null;
+
+ /**
+ * Reference to parent closure.
+ */
+ private Closure _parentClosure = null;
+
+ /**
+ * Cached value of method <code>getCompareValue()</code>.
+ */
+ private Expression _value = null;
+
+ /**
+ * Cached value of method <code>getCompareValue()</code>.
+ */
+ private Step _step = null;
+
+ /**
+ * Initializes a predicate.
+ */
+ public Predicate(Expression exp) {
+ _exp = exp;
+ _exp.setParent(this);
+
+ }
+
+ /**
+ * Set the parser for this expression.
+ */
+ public void setParser(Parser parser) {
+ super.setParser(parser);
+ _exp.setParser(parser);
+ }
+
+ /**
+ * Returns a boolean value indicating if the nth position optimization
+ * is on. Must be call after type checking!
+ */
+ public boolean isNthPositionFilter() {
+ return _nthPositionFilter;
+ }
+
+ /**
+ * Returns a boolean value indicating if the nth descendant optimization
+ * is on. Must be call after type checking!
+ */
+ public boolean isNthDescendant() {
+ return _nthDescendant;
+ }
+
+ /**
+ * Turns off all optimizations for this predicate.
+ */
+ public void dontOptimize() {
+ _canOptimize = false;
+ }
+
+ /**
+ * Returns true if the expression in this predicate contains a call
+ * to position().
+ */
+ public boolean hasPositionCall() {
+ return _exp.hasPositionCall();
+ }
+
+ /**
+ * Returns true if the expression in this predicate contains a call
+ * to last().
+ */
+ public boolean hasLastCall() {
+ return _exp.hasLastCall();
+ }
+
+ // -- Begin Closure interface --------------------
+
+ /**
+ * Returns true if this closure is compiled in an inner class (i.e.
+ * if this is a real closure).
+ */
+ public boolean inInnerClass() {
+ return (_className != null);
+ }
+
+ /**
+ * Returns a reference to its parent closure or null if outermost.
+ */
+ public Closure getParentClosure() {
+ if (_parentClosure == null) {
+ SyntaxTreeNode node = getParent();
+ do {
+ if (node instanceof Closure) {
+ _parentClosure = (Closure) node;
+ break;
+ }
+ if (node instanceof TopLevelElement) {
+ break; // way up in the tree
+ }
+ node = node.getParent();
+ } while (node != null);
+ }
+ return _parentClosure;
+ }
+
+ /**
+ * Returns the name of the auxiliary class or null if this predicate
+ * is compiled inside the Translet.
+ */
+ public String getInnerClassName() {
+ return _className;
+ }
+
+ /**
+ * Add new variable to the closure.
+ */
+ public void addVariable(VariableRefBase variableRef) {
+ if (_closureVars == null) {
+ _closureVars = new ArrayList();
+ }
+
+ // Only one reference per variable
+ if (!_closureVars.contains(variableRef)) {
+ _closureVars.add(variableRef);
+
+ // Add variable to parent closure as well
+ Closure parentClosure = getParentClosure();
+ if (parentClosure != null) {
+ parentClosure.addVariable(variableRef);
+ }
+ }
+ }
+
+ // -- End Closure interface ----------------------
+
+ /**
+ * Returns the node type of the expression owning this predicate. The
+ * return value is cached in <code>_ptype</code>.
+ */
+ public int getPosType() {
+ if (_ptype == -1) {
+ SyntaxTreeNode parent = getParent();
+ if (parent instanceof StepPattern) {
+ _ptype = ((StepPattern)parent).getNodeType();
+ }
+ else if (parent instanceof AbsoluteLocationPath) {
+ AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
+ Expression exp = path.getPath();
+ if (exp instanceof Step) {
+ _ptype = ((Step)exp).getNodeType();
+ }
+ }
+ else if (parent instanceof VariableRefBase) {
+ final VariableRefBase ref = (VariableRefBase)parent;
+ final VariableBase var = ref.getVariable();
+ final Expression exp = var.getExpression();
+ if (exp instanceof Step) {
+ _ptype = ((Step)exp).getNodeType();
+ }
+ }
+ else if (parent instanceof Step) {
+ _ptype = ((Step)parent).getNodeType();
+ }
+ }
+ return _ptype;
+ }
+
+ public boolean parentIsPattern() {
+ return (getParent() instanceof Pattern);
+ }
+
+ public Expression getExpr() {
+ return _exp;
+ }
+
+ public String toString() {
+ return "pred(" + _exp + ')';
+ }
+
+ /**
+ * Type check a predicate expression. If the type of the expression is
+ * number convert it to boolean by adding a comparison with position().
+ * Note that if the expression is a parameter, we cannot distinguish
+ * at compile time if its type is number or not. Hence, expressions of
+ * reference type are always converted to booleans.
+ *
+ * This method may be called twice, before and after calling
+ * <code>dontOptimize()</code>. If so, the second time it should honor
+ * the new value of <code>_canOptimize</code>.
+ */
+ public Type typeCheck(SymbolTable stable) throws TypeCheckError {
+ Type texp = _exp.typeCheck(stable);
+
+ // We need explicit type information for reference types - no good!
+ if (texp instanceof ReferenceType) {
+ _exp = new CastExpr(_exp, texp = Type.Real);
+ }
+
+ // A result tree fragment should not be cast directly to a number type,
+ // but rather to a boolean value, and then to a numer (0 or 1).
+ // Ref. section 11.2 of the XSLT 1.0 spec
+ if (texp instanceof ResultTreeType) {
+ _exp = new CastExpr(_exp, Type.Boolean);
+ _exp = new CastExpr(_exp, Type.Real);
+ texp = _exp.typeCheck(stable);
+ }
+
+ // Numerical types will be converted to a position filter
+ if (texp instanceof NumberType) {
+ // Cast any numerical types to an integer
+ if (texp instanceof IntType == false) {
+ _exp = new CastExpr(_exp, Type.Int);
+ }
+
+ if (_canOptimize) {
+ // Nth position optimization. Expression must not depend on context
+ _nthPositionFilter =
+ !_exp.hasLastCall() && !_exp.hasPositionCall();
+
+ // _nthDescendant optimization - only if _nthPositionFilter is on
+ if (_nthPositionFilter) {
+ SyntaxTreeNode parent = getParent();
+ _nthDescendant = (parent instanceof Step) &&
+ (parent.getParent() instanceof AbsoluteLocationPath);
+ return _type = Type.NodeSet;
+ }
+ }
+
+ // Reset optimization flags
+ _nthPositionFilter = _nthDescendant = false;
+
+ // Otherwise, expand [e] to [position() = e]
+ final QName position =
+ getParser().getQNameIgnoreDefaultNs("position");
+ final PositionCall positionCall =
+ new PositionCall(position);
+ positionCall.setParser(getParser());
+ positionCall.setParent(this);
+
+ _exp = new EqualityExpr(Operators.EQ, positionCall,
+ _exp);
+ if (_exp.typeCheck(stable) != Type.Boolean) {
+ _exp = new CastExpr(_exp, Type.Boolean);
+ }
+ return _type = Type.Boolean;
+ }
+ else {
+ // All other types will be handled as boolean values
+ if (texp instanceof BooleanType == false) {
+ _exp = new CastExpr(_exp, Type.Boolean);
+ }
+ return _type = Type.Boolean;
+ }
+ }
+
+ /**
+ * Create a new "Filter" class implementing
+ * <code>CurrentNodeListFilter</code>. Allocate registers for local
+ * variables and local parameters passed in the closure to test().
+ * Notice that local variables need to be "unboxed".
+ */
+ private void compileFilter(ClassGenerator classGen,
+ MethodGenerator methodGen) {
+ TestGenerator testGen;
+ LocalVariableGen local;
+ FilterGenerator filterGen;
+
+ _className = getXSLTC().getHelperClassName();
+ filterGen = new FilterGenerator(_className,
+ "java.lang.Object",
+ toString(),
+ ACC_PUBLIC | ACC_SUPER,
+ new String[] {
+ CURRENT_NODE_LIST_FILTER
+ },
+ classGen.getStylesheet());
+
+ final ConstantPoolGen cpg = filterGen.getConstantPool();
+ final int length = (_closureVars == null) ? 0 : _closureVars.size();
+
+ // Add a new instance variable for each var in closure
+ for (int i = 0; i < length; i++) {
+ VariableBase var = ((VariableRefBase) _closureVars.get(i)).getVariable();
+
+ filterGen.addField(new Field(ACC_PUBLIC,
+ cpg.addUtf8(var.getEscapedName()),
+ cpg.addUtf8(var.getType().toSignature()),
+ null, cpg.getConstantPool()));
+ }
+
+ final InstructionList il = new InstructionList();
+ testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
+ com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
+ new com.sun.org.apache.bcel.internal.generic.Type[] {
+ com.sun.org.apache.bcel.internal.generic.Type.INT,
+ com.sun.org.apache.bcel.internal.generic.Type.INT,
+ com.sun.org.apache.bcel.internal.generic.Type.INT,
+ com.sun.org.apache.bcel.internal.generic.Type.INT,
+ Util.getJCRefType(TRANSLET_SIG),
+ Util.getJCRefType(NODE_ITERATOR_SIG)
+ },
+ new String[] {
+ "node",
+ "position",
+ "last",
+ "current",
+ "translet",
+ "iterator"
+ },
+ "test", _className, il, cpg);
+
+ // Store the dom in a local variable
+ local = testGen.addLocalVariable("document",
+ Util.getJCRefType(DOM_INTF_SIG),
+ null, null);
+ final String className = classGen.getClassName();
+ il.append(filterGen.loadTranslet());
+ il.append(new CHECKCAST(cpg.addClass(className)));
+ il.append(new GETFIELD(cpg.addFieldref(className,
+ DOM_FIELD, DOM_INTF_SIG)));
+ local.setStart(il.append(new ASTORE(local.getIndex())));
+
+ // Store the dom index in the test generator
+ testGen.setDomIndex(local.getIndex());
+
+ _exp.translate(filterGen, testGen);
+ il.append(IRETURN);
+
+ filterGen.addEmptyConstructor(ACC_PUBLIC);
+ filterGen.addMethod(testGen);
+
+ getXSLTC().dumpClass(filterGen.getJavaClass());
+ }
+
+ /**
+ * Returns true if the predicate is a test for the existance of an
+ * element or attribute. All we have to do is to get the first node
+ * from the step, check if it is there, and then return true/false.
+ */
+ public boolean isBooleanTest() {
+ return (_exp instanceof BooleanExpr);
+ }
+
+ /**
+ * Method to see if we can optimise the predicate by using a specialised
+ * iterator for expressions like '/foo/bar[@attr = $var]', which are
+ * very common in many stylesheets
+ */
+ public boolean isNodeValueTest() {
+ if (!_canOptimize) return false;
+ return (getStep() != null && getCompareValue() != null);
+ }
+
+ /**
+ * Returns the step in an expression of the form 'step = value'.
+ * Null is returned if the expression is not of the right form.
+ * Optimization if off if null is returned.
+ */
+ public Step getStep() {
+ // Returned cached value if called more than once
+ if (_step != null) {
+ return _step;
+ }
+
+ // Nothing to do if _exp is null
+ if (_exp == null) {
+ return null;
+ }
+
+ // Ignore if not equality expression
+ if (_exp instanceof EqualityExpr) {
+ EqualityExpr exp = (EqualityExpr)_exp;
+ Expression left = exp.getLeft();
+ Expression right = exp.getRight();
+
+ // Unwrap and set _step if appropriate
+ if (left instanceof CastExpr) {
+ left = ((CastExpr) left).getExpr();
+ }
+ if (left instanceof Step) {
+ _step = (Step) left;
+ }
+
+ // Unwrap and set _step if appropriate
+ if (right instanceof CastExpr) {
+ right = ((CastExpr)right).getExpr();
+ }
+ if (right instanceof Step) {
+ _step = (Step)right;
+ }
+ }
+ return _step;
+ }
+
+ /**
+ * Returns the value in an expression of the form 'step = value'.
+ * A value may be either a literal string or a variable whose
+ * type is string. Optimization if off if null is returned.
+ */
+ public Expression getCompareValue() {
+ // Returned cached value if called more than once
+ if (_value != null) {
+ return _value;
+ }
+
+ // Nothing to to do if _exp is null
+ if (_exp == null) {
+ return null;
+ }
+
+ // Ignore if not an equality expression
+ if (_exp instanceof EqualityExpr) {
+ EqualityExpr exp = (EqualityExpr) _exp;
+ Expression left = exp.getLeft();
+ Expression right = exp.getRight();
+
+ // Return if left is literal string
+ if (left instanceof LiteralExpr) {
+ _value = left;
+ return _value;
+ }
+ // Return if left is a variable reference of type string
+ if (left instanceof VariableRefBase &&
+ left.getType() == Type.String)
+ {
+ _value = left;
+ return _value;
+ }
+
+ // Return if right is literal string
+ if (right instanceof LiteralExpr) {
+ _value = right;
+ return _value;
+ }
+ // Return if left is a variable reference whose type is string
+ if (right instanceof VariableRefBase &&
+ right.getType() == Type.String)
+ {
+ _value = right;
+ return _value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Translate a predicate expression. This translation pushes
+ * two references on the stack: a reference to a newly created
+ * filter object and a reference to the predicate's closure.
+ */
+ public void translateFilter(ClassGenerator classGen,
+ MethodGenerator methodGen)
+ {
+ final ConstantPoolGen cpg = classGen.getConstantPool();
+ final InstructionList il = methodGen.getInstructionList();
+
+ // Compile auxiliary class for filter
+ compileFilter(classGen, methodGen);
+
+ // Create new instance of filter
+ il.append(new NEW(cpg.addClass(_className)));
+ il.append(DUP);
+ il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
+ "<init>", "()V")));
+
+ // Initialize closure variables
+ final int length = (_closureVars == null) ? 0 : _closureVars.size();
+
+ for (int i = 0; i < length; i++) {
+ VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
+ VariableBase var = varRef.getVariable();
+ Type varType = var.getType();
+
+ il.append(DUP);
+
+ // Find nearest closure implemented as an inner class
+ Closure variableClosure = _parentClosure;
+ while (variableClosure != null) {
+ if (variableClosure.inInnerClass()) break;
+ variableClosure = variableClosure.getParentClosure();
+ }
+
+ // Use getfield if in an inner class
+ if (variableClosure != null) {
+ il.append(ALOAD_0);
+ il.append(new GETFIELD(
+ cpg.addFieldref(variableClosure.getInnerClassName(),
+ var.getEscapedName(), varType.toSignature())));
+ }
+ else {
+ // Use a load of instruction if in translet class
+ il.append(var.loadInstruction());
+ }
+
+ // Store variable in new closure
+ il.append(new PUTFIELD(
+ cpg.addFieldref(_className, var.getEscapedName(),
+ varType.toSignature())));
+ }
+ }
+
+ /**
+ * Translate a predicate expression. If non of the optimizations apply
+ * then this translation pushes two references on the stack: a reference
+ * to a newly created filter object and a reference to the predicate's
+ * closure. See class <code>Step</code> for further details.
+ */
+ public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
+
+ final ConstantPoolGen cpg = classGen.getConstantPool();
+ final InstructionList il = methodGen.getInstructionList();
+
+ if (_nthPositionFilter || _nthDescendant) {
+ _exp.translate(classGen, methodGen);
+ }
+ else if (isNodeValueTest() && (getParent() instanceof Step)) {
+ _value.translate(classGen, methodGen);
+ il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
+ il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
+ }
+ else {
+ translateFilter(classGen, methodGen);
+ }
+ }
+}