Package client.net.sf.saxon.ce.expr

Source Code of client.net.sf.saxon.ce.expr.UserFunctionCall

package client.net.sf.saxon.ce.expr;

import client.net.sf.saxon.ce.event.SequenceReceiver;
import client.net.sf.saxon.ce.expr.instruct.UserFunction;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.SequenceIterator;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.om.ValueRepresentation;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.AnyItemType;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.*;


/**
* This class represents a call to a user-defined function in the stylesheet or query.
*/

public class UserFunctionCall extends FunctionCall {

    private SequenceType staticType;
    private UserFunction function;
    private boolean tailCall = false;
        // indicates only that this is a tail call, not necessarily a recursive tail call
    private int[] argumentEvaluationModes = null;

    /**
     * Create a function call to a user-written function in a query or stylesheet
     */

    public UserFunctionCall() {}

    /**
     * Set the static type
     * @param type the static type of the result of the function call
     */

    public void setStaticType(SequenceType type) {
        staticType = type;
    }

    /**
     * Create the reference to the function to be called
     * @param compiledFunction the function being called
     */

    public void setFunction(UserFunction compiledFunction) {
        function = compiledFunction;
    }

    /**
     * Check the function call against the declared function signature
     * @param compiledFunction the function being called
     * @param visitor an expression visitor
     */

    public void checkFunctionCall(UserFunction compiledFunction,
                            ExpressionVisitor visitor) throws XPathException {
        int n = compiledFunction.getNumberOfArguments();
        for (int i=0; i<n; i++) {
            RoleLocator role = new RoleLocator(
                    RoleLocator.FUNCTION, compiledFunction.getFunctionName(), i);
            role.setErrorCode("XTTE0790");
            argument[i] = TypeChecker.staticTypeCheck(
                                argument[i],
                                compiledFunction.getArgumentType(i),
                                false,
                                role, visitor);
        }
    }


    /**
    * Method called during the type checking phase
    */

    public void checkArguments(ExpressionVisitor visitor) throws XPathException {
        // these checks are now done in setFunction(), at the time when the function
        // call is bound to an actual function
    }

   /**
     * Get the qualified of the function being called
     * @return the qualified name
     */

    public final StructuredQName getFunctionName() {
        StructuredQName n = super.getFunctionName();
        if (n == null) {
            return function.getFunctionName();
        } else {
            return n;
        }
    }

    /**
    * Pre-evaluate a function at compile time. This version of the method suppresses
    * early evaluation by doing nothing.
     * @param visitor an expression visitor
     */

    public Expression preEvaluate(ExpressionVisitor visitor) {
        return this;
    }

    /**
    * Determine the data type of the expression, if possible
    * @return Type.ITEM (meaning not known in advance)
     * @param th the type hierarchy cache
     */

    public ItemType getItemType(TypeHierarchy th) {
        if (staticType == null) {
            // the actual type is not known yet, so we return an approximation
            return AnyItemType.getInstance();
        } else {
            return staticType.getPrimaryType();
        }
    }

    public int getIntrinsicDependencies() {
        return StaticProperty.DEPENDS_ON_USER_FUNCTIONS;
    }

    /**
    * Determine the cardinality of the result
    */

    public int computeCardinality() {
        if (staticType == null) {
            // the actual type is not known yet, so we return an approximation
            return StaticProperty.ALLOWS_ZERO_OR_MORE;
        } else {
            return staticType.getCardinality();
        }
    }

    public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
        Expression e = super.typeCheck(visitor, contextItemType);
        if (function != null) {
            if (e == this) {
                computeArgumentEvaluationModes();
            }
            if (staticType == SequenceType.ANY_SEQUENCE) {
                // try to get a better type
                staticType = function.getResultType(visitor.getConfiguration().getTypeHierarchy());
            }
        }
        return e;
    }

    public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
        Expression e = super.optimize(visitor, contextItemType);
        if (e == this && function != null) {
            computeArgumentEvaluationModes();
        }
        return e;
    }

    /**
    * Promote this expression if possible
    */

    public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
        Expression exp = offer.accept(parent, this);
        if (exp != null) {
            return exp;
        } else {
            boolean changed = false;
            if (offer.action != PromotionOffer.UNORDERED) {
                for (int i=0; i<argument.length; i++) {
                    Expression a2 = doPromotion(argument[i], offer);
                    changed |= (a2 != argument[i]);
                    argument[i] = a2;
                }
            }
            if (changed && function != null) {
                computeArgumentEvaluationModes();
            }
            return this;
        }
    }

    /**
     * Compute the evaluation mode of each argument
     */

    public void computeArgumentEvaluationModes() {
        argumentEvaluationModes = new int[argument.length];
        for (int i=0; i<argument.length; i++) {
            int refs = function.getParameterDefinitions()[i].getReferenceCount();
            if (refs == 0) {
                // the argument is never referenced, so don't evaluate it
                argumentEvaluationModes[i] = ExpressionTool.RETURN_EMPTY_SEQUENCE;
            } else if (function.getParameterDefinitions()[i].isIndexedVariable()) {
                argumentEvaluationModes[i] = ExpressionTool.MAKE_INDEXED_VARIABLE;
            } else if ((argument[i].getDependencies() & StaticProperty.DEPENDS_ON_USER_FUNCTIONS) != 0) {
                // if the argument contains a call to a user-defined function, then it might be a recursive call.
                // It's better to evaluate it now, rather than waiting until we are on a new stack frame, as
                // that can blow the stack if done repeatedly. (See test func42)
                argumentEvaluationModes[i] = ExpressionTool.eagerEvaluationMode(argument[i]);
            } else {
                int m = ExpressionTool.lazyEvaluationMode(argument[i]);
                if (m == ExpressionTool.MAKE_CLOSURE && refs > 1) {
                    m = ExpressionTool.MAKE_MEMO_CLOSURE;
                }
                argumentEvaluationModes[i] = m;
            }
        }
    }


    /**
     * Mark tail-recursive calls on stylesheet functions. This marks the function call as tailRecursive if
     * if is a call to the containing function, and in this case it also returns "true" to the caller to indicate
     * that a tail call was found.
    */

    public int markTailFunctionCalls(StructuredQName qName, int arity) {
        tailCall = true;
        return (getFunctionName().equals(qName) &&
               arity == getNumberOfArguments() ? 2 : 1);
    }

    public int getImplementationMethod() {
        if (Cardinality.allowsMany(getCardinality())) {
            return ITERATE_METHOD | PROCESS_METHOD;
        } else {
            return EVALUATE_METHOD;
        }
    }

    /**
    * Call the function, returning the value as an item. This method will be used
    * only when the cardinality is zero or one. If the function is tail recursive,
    * it returns an Object representing the arguments to the next (recursive) call
    */

    public Item evaluateItem(XPathContext c) throws XPathException {
        ValueRepresentation val = callFunction(c);
        return Value.asItem(val);
    }

    /**
    * Call the function, returning an iterator over the results. (But if the function is
    * tail recursive, it returns an iterator over the arguments of the recursive call)
    */

    public SequenceIterator iterate(XPathContext c) throws XPathException {
        ValueRepresentation result = callFunction(c);
        return Value.getIterator(result);
    }


    /**
     * This is the method that actually does the function call
     * @param c the dynamic context
     * @return the result of the function
     * @throws XPathException if dynamic errors occur
     */
    private ValueRepresentation callFunction(XPathContext c) throws XPathException {
        ValueRepresentation[] actualArgs = evaluateArguments(c);

        if (tailCall) {
            ((XPathContextMajor)c).requestTailCall(function, actualArgs);
            return EmptySequence.getInstance();
        }

        XPathContextMajor c2 = c.newCleanContext();
        c2.setTemporaryOutputState(true);
        return function.call(actualArgs, c2);
    }

    /**
     * Process the function call in push mode
     * @param context the XPath dynamic context
     * @throws XPathException
     */

    public void process(XPathContext context) throws XPathException {
        ValueRepresentation[] actualArgs = evaluateArguments(context);
        if (tailCall) {
            ((XPathContextMajor)context).requestTailCall(function, actualArgs);
        } else {
            SequenceReceiver out = context.getReceiver();
            XPathContextMajor c2 = context.newCleanContext();
            c2.setReceiver(out);
            function.process(actualArgs, c2);
        }
    }


    private ValueRepresentation[] evaluateArguments(XPathContext c) throws XPathException {
        int numArgs = argument.length;
        ValueRepresentation[] actualArgs = new ValueRepresentation[numArgs];
        if (argumentEvaluationModes == null) {
            // should have been done at compile time
            computeArgumentEvaluationModes();
        }
        for (int i=0; i<numArgs; i++) {

            int refs = function.getParameterDefinitions()[i].getReferenceCount();
            actualArgs[i] = ExpressionTool.evaluate(argument[i], argumentEvaluationModes[i], c, refs);

            if (actualArgs[i] == null) {
                actualArgs[i] = EmptySequence.getInstance();
            }
            // If the argument has come in as a (non-memo) closure but there are multiple references to it,
            // then we materialize it in memory now. This shouldn't really happen but it does (tour.xq)
            if (refs > 1 && actualArgs[i] instanceof Closure && !(actualArgs[i] instanceof MemoClosure)) {
                actualArgs[i] = ((Closure)actualArgs[i]).reduce();
            }
        }
        return actualArgs;
    }

    /**
     * Call the function dynamically. For this to be possible, the static arguments of the function call
     * must have been set up as {@link SuppliedParameterReference} objects. The actual arguments are placed on the
     * callee's stack, and the type conversion takes place "in situ".
     * @param suppliedArguments the values to be used for the arguments of the function
     * @param context the dynamic evaluation context
     * @return the result of evaluating the function
     */

    public ValueRepresentation dynamicCall(ValueRepresentation[] suppliedArguments, XPathContext context) throws XPathException {
        ValueRepresentation[] convertedArgs = new ValueRepresentation[suppliedArguments.length];
        XPathContextMajor c2 = context.newCleanContext();
        c2.setCaller(context);
        c2.openStackFrame(suppliedArguments.length);
        for (int i=0; i<suppliedArguments.length; i++) {
            c2.setLocalVariable(i, suppliedArguments[i]);
            convertedArgs[i] = ExpressionTool.lazyEvaluate(argument[i], c2, 10);
        }
        XPathContextMajor c3 = c2.newCleanContext();
        return function.call(convertedArgs, c3);
    }

    public StructuredQName getObjectName() {
        return getFunctionName();
    }

}

// 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.
TOP

Related Classes of client.net.sf.saxon.ce.expr.UserFunctionCall

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.