All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.expr.parser.PromotionOffer Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.expr.parser;

import net.sf.saxon.expr.*;
import net.sf.saxon.expr.instruct.LocalParamSetter;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.GlobalVariableManager;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.SequenceType;

/**
 * PromotionOffer is an object used transiently during compilation of an expression. It contains
 * information passed by a containing expression to its subexpressions, when looking for subexpressions
 * that can be promoted to a higher level because they are not dependent on the context established
 * by the containing expression. The object is also used to pass back return information when the
 * promotion actually takes place.
 */

public class PromotionOffer {

    /**
     * FOCUS_INDEPENDENT requests promotion of all non-trivial subexpressions that don't depend on the
     * focus. This is typically used to extract subexpressions from a filter predicate. The offer is
     * optional - each subexpression can decide whether it's worth the trouble of promoting itself.
     * The offer is normally passed on to subexpressions, except subexpressions that are evaluated
     * with a different focus
     */

    public static final int FOCUS_INDEPENDENT = 10;

    /**
     * RANGE_INDEPENDENT requests promotion of all non-trivial subexpressions that don't depend on a
     * specified range variable. This is typically used to extract subexpressions from the action of
     * a for expression or the condition of a some/every quantified expression. The offer is
     * optional - each subexpression can decide whether it's worth the trouble of promoting itself.
     * The offer is normally passed on to subexpressions, except subexpressions that are evaluated
     * with a different focus or a different range variable, because these may have other dependencies
     * that prevent their promotion.
     */

    public static final int RANGE_INDEPENDENT = 11;

    /**
     * EXTRACT_GLOBAL_VARIABLES identifies subexpressions that are not constant, but have no dependencies
     * other than on global variables or parameters (they must also be non-creative). Such expressions can
     * be extracted from a function or template and converted into global variables. This optimization is done
     * in Saxon-EE only.
     */

    public static final int EXTRACT_GLOBAL_VARIABLES = 14;

    /**
     * The optimizer in use
     */

    private Optimizer optimizer;

    /**
     * The expression visitor in use
     */

    public ExpressionVisitor visitor;

    /**
     * action is one of the possible promotion actions, FOCUS_INDEPENDENT, RANGE_INDEPENDENT,
     * EXTRACT_GLOBAL_VARIABLES
     */

    public int action;

    /**
     * In the case of FOCUS_INDEPENDENT, "promoteDocumentDependent" is a boolean that, when set to
     * true, indicates that it is safe to promote a subexpression that depends on the context document
     * but not on other aspects of the focus. This is the case, for example, in a filter expression when
     * it is known that all the nodes selected by the expression will be in the same document - as happens
     * when the filter is applied to a path expression. This allows subexpressions such as key() to be
     * promoted
     */

    public boolean promoteDocumentDependent = false;

    /**
     * In the case of FOCUS_INDEPENDENT, "promoteXSLTFunctions" is a boolean that, when set to true, indicates
     * that it is safe to promote XSLT functions such as current(). This flag is set when rewriting XPath expressions
     * and is unset when rewriting XSLT templates.
     */

    public boolean promoteXSLTFunctions = true;

    /**
     * In the case of EXTRACT_GLOBAL_VARIABLES, globalVariableManager is the set of global variables
     * to which any new global variables can be added
     */

    public GlobalVariableManager globalVariableManager;

    /**
     * In the case of RANGE_INDEPENDENT, "binding" identifies the range variables whose dependencies
     * we are looking for. For INLINE_VARIABLE_REFERENCES it is a single Binding that we are aiming to inline
     */

    /*@Nullable*/ public Binding[] bindingList;

    /**
     * When a promotion offer is made, containingExpression identifies the level to which the promotion
     * should occur. When a subexpression is promoted, an expression of the form let $VAR := SUB return ORIG
     * is created, and this replaces the original containingExpression within the PromotionOffer.
     */

    public Expression containingExpression;

    /**
     * Flag that is set if the offer has been accepted, that is, if the expression has changed
     */

    public boolean accepted = false;

    /**
     * Create a PromotionOffer for use with a particular Optimizer
     *
     * @param optimizer the optimizer
     */

    public PromotionOffer(Optimizer optimizer) {
        this.optimizer = optimizer;
    }

    /**
     * Get the optimizer in use
     *
     * @return the optimizer
     */

    public Optimizer getOptimizer() {
        return optimizer;
    }

    /**
     * Method to test whether a subexpression qualifies for promotion, and if so, to
     * accept the promotion.
     *
     *
     * @param child  the subexpression in question
     * @return if promotion was done, returns the expression that should be used in place
     *         of the child expression. If no promotion was done, returns null. If promotion is
     *         determined not to be necessary for this subtree, returns the supplied child expression
     *         unchanged
     */

    public Expression accept(Expression child) throws XPathException {
        switch (action) {
            case RANGE_INDEPENDENT: {
                int properties = child.getSpecialProperties();
                if (((properties & StaticProperty.NON_CREATIVE) != 0) &&
                        !ExpressionTool.dependsOnVariable(child, bindingList) &&
                        (child.getDependencies() &
                                (StaticProperty.HAS_SIDE_EFFECTS | StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS)) == 0) {
                    return promote(child);
                }
                break;
            }

            case FOCUS_INDEPENDENT: {
                int dependencies = child.getDependencies();
                int properties = child.getSpecialProperties();
                if (!promoteXSLTFunctions && ((dependencies & StaticProperty.DEPENDS_ON_XSLT_CONTEXT) != 0)) {
                    break;
                }
//                if (ExpressionTool.dependsOnVariable(child, bindingList, "PF")) {
//                    break;
//                }
                if ((dependencies &
                        (StaticProperty.HAS_SIDE_EFFECTS | StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS)) != 0) {
                    break;
                }
                if ((dependencies & StaticProperty.DEPENDS_ON_FOCUS) == 0 &&
                        (properties & StaticProperty.NON_CREATIVE) != 0 &&
                        !ExpressionTool.dependsOnVariable(child, bindingList)) {
                    return promote(child);
                } else if (promoteDocumentDependent &&
                        (dependencies & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) == 0 &&
                        (properties & StaticProperty.NON_CREATIVE) != 0 &&
                        !ExpressionTool.dependsOnVariable(child, bindingList)) {
                    return promote(child);
                }
                break;
            }

            case EXTRACT_GLOBAL_VARIABLES:
                if (!(child instanceof Literal || child instanceof LocalParamSetter ||
                        (child == containingExpression) /*||
                        ExpressionTool.containsLocalParam(child)*/) &&
                        (child.getDependencies() & ~StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT) == 0 &&
                        (child.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0 &&
                        (child.getSpecialProperties() & StaticProperty.HAS_SIDE_EFFECTS) == 0) {
                    return optimizer.extractGlobalVariables(child, visitor, this);
                }
                break;

            default:
                throw new UnsupportedOperationException("Unknown promotion action " + action);
        }
        return null;
    }

    /**
     * Method to promote a subexpression. A LetExpression is created which binds the child expression
     * to a system-created variable, and then returns the original expression, with the child expression
     * replaced by a reference to the variable.
     *
     *
     * @param child the expression to be promoted
     * @return the expression that results from the promotion, if any took place
     */

    private Expression promote(Expression child) {

        // if the expression being promoted is an operand of "=", make the variable an indexed variable
        boolean indexed = false;
        Expression parent = child.getParentExpression();
        if (parent instanceof GeneralComparison && ((GeneralComparison) parent).getOperator() == Token.EQUALS) {
            indexed = true;
        }

        LetExpression let = new LetExpression();
        let.setVariableQName(new StructuredQName("vv", NamespaceConstant.SAXON_GENERATED_VARIABLE, "loc" + let.hashCode()));
        SequenceType type = SequenceType.makeSequenceType(child.getItemType(), child.getCardinality());
        let.setRequiredType(type);
        ExpressionTool.copyLocationInfo(containingExpression, let);
        //let.setSequence(LazyExpression.makeLazyExpression(child));
        let.setSequence(child);
        let.setEvaluationMode(
                Cardinality.allowsMany(child.getCardinality()) ?
                        ExpressionTool.MAKE_MEMO_CLOSURE :
                        ExpressionTool.MAKE_SINGLETON_CLOSURE);
        let.setAction(containingExpression);
        let.adoptChildExpression(containingExpression);
        //let.setParentExpression(containingExpression.getParentExpression());
        //containingExpression.setParentExpression(let);
        if (indexed) {
            let.setIndexedVariable();
        }
        containingExpression = let;
        accepted = true;
        LocalVariableReference var = new LocalVariableReference(let);
        int properties = child.getSpecialProperties() & StaticProperty.NOT_UNTYPED_ATOMIC;
        var.setStaticType(type, null, properties);
        let.addReference(var, true);
        ExpressionTool.copyLocationInfo(containingExpression, var);
        return var;
    }


}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy