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

net.sf.saxon.expr.UserFunctionCall Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 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;

import net.sf.saxon.event.Outputter;
import net.sf.saxon.expr.elab.*;
import net.sf.saxon.expr.instruct.Block;
import net.sf.saxon.expr.instruct.TailCall;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.om.*;
import net.sf.saxon.query.UnboundFunctionLibrary;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.SymbolicName;
import net.sf.saxon.trans.Visibility;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.iter.EmptyIterator;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.UType;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.SequenceType;

import java.util.ArrayList;
import java.util.List;


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

public class UserFunctionCall extends FunctionCall implements UserFunctionResolvable, ComponentInvocation, ContextOriginator {

    private SequenceType staticType;
    private UserFunction function;
    private int bindingSlot = -1;
    private int tailCall = NOT_TAIL_CALL;
    private StructuredQName name;
    private boolean beingInlined = false;
    private SequenceEvaluator[] argumentEvaluators = null;
    private UnboundFunctionLibrary.UnboundFunctionCallDetails unboundCallDetails;



    public boolean isBeingInlined() {
        return beingInlined;
    }

    public void setBeingInlined(boolean beingInlined) {
        this.beingInlined = beingInlined;
    }

    public static final int NOT_TAIL_CALL = 0;
    public static final int FOREIGN_TAIL_CALL = 1;
    public static final int SELF_TAIL_CALL = 2;

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

    public UserFunctionCall() {
    }

    /**
     * Create an unbound function call (typically, a forwards reference in XQuery)
     */

    public UserFunctionCall(UnboundFunctionLibrary.UnboundFunctionCallDetails details) {
        this.unboundCallDetails = details;
    }

    /**
     * Copy details from another user function call
     */

    public void copyFrom(UserFunctionCall ufc2) {
        staticType = ufc2.staticType;
        function = ufc2.function;
        bindingSlot = ufc2.bindingSlot;
        tailCall = ufc2.tailCall;
        name = ufc2.name;
        beingInlined = false;
        argumentEvaluators = ufc2.argumentEvaluators;
        unboundCallDetails = null;
    }


    /**
     * Set the name of the function being called
     *
     * @param name the name of the function
     */

    public final void setFunctionName(StructuredQName name) {
        this.name = name;
    }


    /**
     * 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
     */

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

    /**
     * Set the binding slot to be used. This is the offset within the binding vector of the containing
     * component where the actual target template is to be found. The target function is not held directly
     * in the UserFunctionCall expression itself because it can be overridden in a using package.
     *
     * @param slot the offset in the binding vector of the containing package where the target template
     *             can be found.
     */


    @Override
    public void setBindingSlot(int slot) {
        this.bindingSlot = slot;
    }

    /**
     * Get the binding slot to be used. This is the offset within the binding vector of the containing
     * component where the actual target template is to be found.
     *
     * @return the offset in the binding vector of the containing package where the target template
     * can be found.
     */

    @Override
    public int getBindingSlot() {
        return bindingSlot;
    }


    /**
     * Get the function that is being called by this function call. This is the provisional
     * binding: the actual function might be an override of this one.
     *
     * @return the function being called
     */

    public UserFunction getFunction() {
        return function;
    }

    @Override
    public Component getFixedTarget() {
        Visibility v = function.getDeclaringComponent().getVisibility();
        if (v == Visibility.PRIVATE || v == Visibility.FINAL) {
            return function.getDeclaringComponent();
        } else {
            return null;
        }
    }

    public UnboundFunctionLibrary.UnboundFunctionCallDetails getUnboundCallDetails() {
        return unboundCallDetails;
    }

    /**
     * Determine whether this is a tail call (not necessarily a recursive tail call)
     *
     * @return true if this function call is a tail call
     */

    public boolean isTailCall() {
        return tailCall != NOT_TAIL_CALL;
    }

    public boolean isRecursiveTailCall() {
        return tailCall == SELF_TAIL_CALL;
    }

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

    @Override
    public final StructuredQName getFunctionName() {
        if (name == null) {
            return function.getFunctionName();
        } else {
            return name;
        }
    }

    @Override
    public SymbolicName getSymbolicName() {
        return new SymbolicName.F(getFunctionName(), getArity());
    }

    public Component getTarget() {
        return function.getDeclaringComponent();
    }


    private static final int UNHANDLED_DEPENDENCIES =
        StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST |
            StaticProperty.DEPENDS_ON_XSLT_CONTEXT | StaticProperty.DEPENDS_ON_USER_FUNCTIONS;


    public void allocateArgumentEvaluators() {
        argumentEvaluators = new SequenceEvaluator[getArity()];
        UserFunction target = getFunction();
        int i = 0;
        for (Operand o : operands()) {
            Expression arg = o.getChildExpression();
            if (arg instanceof ErrorExpression && ((ErrorExpression)arg).getErrorCodeLocalPart().equals("UseDefault")) {
                arg = target.getDefaultValueExpression(i);
                o.setChildExpression(arg);
            }
            if (i == 0 && target.getDeclaredStreamability().isConsuming()) {
                argumentEvaluators[i] = new StreamingArgumentEvaluator(arg);
            } else if (target.getParameterDefinitions()[i].isIndexedVariable()) {
                PullEvaluator argPull = arg.makeElaborator().elaborateForPull();
                argumentEvaluators[i] = new IndexedVariableEvaluator(argPull);
            } else if ((arg.getDependencies() & UNHANDLED_DEPENDENCIES) != 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)
                // If the argument contains calls to position(), last(), regex-group(), current-group(),
                // current-merge-group(), etc, then in general we can't save the values in a Closure
                // so we need to evaluate the argument eagerly. (Tests position-0103, merge-096).
                argumentEvaluators[i] = arg.makeElaborator().eagerly();
//            } else if (!Cardinality.allowsMany(arg.getCardinality()) && arg.getCost() < 20) {
//                // the argument is cheap to evaluate and doesn't use much memory...
//                argumentEvaluators[i] = arg.makeElaborator().eagerly();
//            } else if (arg.getCardinality() == StaticProperty.ALLOWS_ZERO_OR_ONE) {
//                ItemEvaluator argEval = arg.makeElaborator().elaborateForItem();
//                argumentEvaluators[i] = new OptionalItemEvaluator(argEval);
            } else if (arg instanceof Block && ((Block) arg).isCandidateForSharedAppend()) {
                // If the expression is a Block, that is, it is appending a value to a sequence,
                // then we have the opportunity to use a shared list underpinning the old value and
                // the new. This takes precedence over lazy evaluation (it would be possible to do this
                // lazily, but more difficult). We currently do this for any Block that has a variable
                // reference as one of its subexpressions. The most common case is that the first argument is a reference
                // to an argument of recursive function, where the recursive function returns the result of
                // appending to the sequence.
                argumentEvaluators[i] = new SharedAppendEvaluator((Block) arg);
            } else {
                argumentEvaluators[i] = new LearningEvaluator(
                        arg, arg.makeElaborator().lazily(true));
            }
            i++;
        }
    }

    public SequenceEvaluator[] getArgumentEvaluators() {
        return argumentEvaluators;
    }

//    public static class ArgumentEvaluationModeAdjuster implements Consumer {
//
//        private final UserFunctionCall functionCall;
//        private final int argument;
//        private int completed;
//        private int abandoned;
//
//        public ArgumentEvaluationModeAdjuster(UserFunctionCall functionCall, int argument) {
//            this.functionCall = functionCall;
//            this.argument = argument;
//        }
//
//        @Override
//        public void accept(MemoClosure.MemoClosureStatistics statistics) {
//            if (functionCall.argumentEvaluators[argument].getCode() == Evaluators.MAKE_MEMO_CLOSURE) {
//                if (statistics.allRead) {
//                    completed++;
//                } else {
//                    abandoned++;
//                }
//                if (completed > 10 && abandoned == 0) {
//                    //System.err.println("Argument evaluator set to eager");
//                    functionCall.argumentEvaluators[argument] = Evaluator.EagerSequence.INSTANCE;
//                }
//            }
//        }
//
//    }


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

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

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

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

    /**
     * Get the static type of the expression as a UType, following precisely the type
     * inference rules defined in the XSLT 3.0 specification.
     *
     * @param contextItemType the static type of the context item
     * @return the static item type of the expression according to the XSLT 3.0 defined rules
     */
    @Override
    public UType getStaticUType(UType contextItemType) {
        UserFunction f = getFunction();
        if (f == null) {
            // Happens when called during parsing
            return UType.ANY;
        }
        return f.getResultType().getPrimaryType().getUType();
    }

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

    /**
     * Determine whether this is an updating expression as defined in the XQuery update specification
     *
     * @return true if this is an updating expression
     */

    @Override
    public boolean isUpdatingExpression() {
        return function.isUpdating();
    }

    /**
     * Compute the special properties of this expression. These properties are denoted by a bit-significant
     * integer, possible values are in class {@link net.sf.saxon.expr.StaticProperty}. The "special" properties are properties
     * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
     * example whether the nodes are in document order.
     *
     * @return the special properties, as a bit-significant integer
     */

    @Override
    protected int computeSpecialProperties() {
        // Inherit the properties of the function being called if possible. But we have to prevent
        // looping when the function is recursive. For safety, we only consider the properties of the
        // function body if it contains no further function calls. Also, we can only do this safely if
        // the function is private or final
        if (function == null) {
            return super.computeSpecialProperties();
        } else if (function.getBody() != null &&
                (function.getDeclaredVisibility() == Visibility.PRIVATE || function.getDeclaredVisibility() == Visibility.FINAL)) {
            int props;
            List calledFunctions = new ArrayList<>();
            ExpressionTool.gatherCalledFunctions(function.getBody(), calledFunctions);
            if (calledFunctions.isEmpty()) {
                props = function.getBody().getSpecialProperties();
            } else {
                props = super.computeSpecialProperties();
            }
            if (function.getDeterminism() != UserFunction.Determinism.PROACTIVE) {
                props |= StaticProperty.NO_NODES_NEWLY_CREATED;
            }
            return props;
        } else {
            return super.computeSpecialProperties();
        }
    }

    /**
     * Copy an expression. This makes a deep copy.
     *
     * @param rebindings variable bindings that need to be changed
     * @return the copy of the original expression
     */

    /*@NotNull*/
    @Override
    public Expression copy(RebindingMap rebindings) {
        if (function == null) {
            // not bound yet, we have no way to register the new copy with the XSLFunction
            throw new UnsupportedOperationException("UserFunctionCall.copy()");
        }
        UserFunctionCall ufc = new UserFunctionCall();
        ufc.setFunction(function);
        ufc.setStaticType(staticType);
        int numArgs = getArity();
        Expression[] a2 = new Expression[numArgs];
        for (int i = 0; i < numArgs; i++) {
            a2[i] = getArg(i).copy(rebindings);
        }
        ufc.setArguments(a2);
        ExpressionTool.copyLocationInfo(this, ufc);
        return ufc;
    }

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

    @Override
    protected 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();
        }
    }

    /*@NotNull*/
    @Override
    public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
        Expression e = super.typeCheck(visitor, contextInfo);
        if (e != this) {
            return e;
        }
        if (function != null) {
            checkFunctionCall(function, visitor);
            if (staticType == null || staticType == SequenceType.ANY_SEQUENCE) {
                // try to get a better type
                staticType = function.getResultType();
            }
        }
        return this;
    }

    /*@NotNull*/
    @Override
    public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo contextItemType) throws XPathException {
        Expression e = super.optimize(visitor, contextItemType);
        if (e == this && function != null) {
            return visitor.obtainOptimizer().tryInlineFunctionCall(
                    this, visitor, contextItemType);
        }
        return e;
    }

    /**
     * Reset the static properties of the expression to -1, so that they have to be recomputed
     * next time they are used.
     */
    @Override
    public void resetLocalStaticProperties() {
        super.resetLocalStaticProperties();
        //argumentEvaluators = null;
    }

    /**
     * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
     * by an expression in a source tree.
     * 

The default implementation of this method assumes that an expression does no navigation other than * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the * same context as the containing expression. The method must be overridden for any expression * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression, * and RootExpression (because they perform navigation), and for the doc(), document(), and collection() * functions because they create a new navigation root. Implementations also exist for PathExpression and * FilterExpression because they have subexpressions that are evaluated in a different context from the * calling expression.

* * @param pathMap the PathMap to which the expression should be added * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added * @return the pathMapNode representing the focus established by this expression, in the case where this * expression is the first operand of a path expression or filter expression. For an expression that does * navigation, it represents the end of the arc in the path map that describes the navigation route. For other * expressions, it is the same as the input pathMapNode. */ @Override public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) { return addExternalFunctionCallToPathMap(pathMap, pathMapNodeSet); } /** * 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. */ @Override public int markTailFunctionCalls(StructuredQName qName, int arity) { tailCall = getFunctionName().equals(qName) && arity == getArity() ? SELF_TAIL_CALL : FOREIGN_TAIL_CALL; return tailCall; } @Override 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 */ @Override public Item evaluateItem(XPathContext c) throws XPathException { return makeElaborator().elaborateForItem().eval(c); } /** * 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) */ /*@NotNull*/ @Override public SequenceIterator iterate(XPathContext c) throws XPathException { return makeElaborator().elaborateForPull().iterate(c); } private void requestTailCall(XPathContext context, Sequence[] actualArgs) throws XPathException { if (bindingSlot >= 0) { TailCallLoop.TailCallComponent info = new TailCallLoop.TailCallComponent(); Component target = getTargetComponent(context); info.component = target; info.function = (UserFunction) target.getActor(); if (target.isHiddenAbstractComponent()) { throw new XPathException("Cannot call an abstract function (" + name.getDisplayName() + ") with no implementation", "XTDE3052"); } ((XPathContextMajor) context).requestTailCall(info, actualArgs); } else { TailCallLoop.TailCallFunction info = new TailCallLoop.TailCallFunction(); info.function = function; ((XPathContextMajor) context).requestTailCall(info, actualArgs); } } /** * Process the function call in push mode * * * @param output the destination for the result * @param context the XPath dynamic context * @throws XPathException if a dynamic error occurs */ @Override public void process(Outputter output, XPathContext context) throws XPathException { TailCall tc = makeElaborator().elaborateForPush().processLeavingTail(output, context); dispatchTailCall(tc); } public Component getTargetComponent(XPathContext context) { if (bindingSlot == -1) { // fallback for non-package code return function.getDeclaringComponent(); } else { return context.getTargetComponent(bindingSlot); } } @Override public UserFunction getTargetFunction(XPathContext context) { return (UserFunction) getTargetComponent(context).getActor(); } public Sequence[] evaluateArguments(XPathContext c, boolean streamed) throws XPathException { int numArgs = getArity(); Sequence[] actualArgs = SequenceTool.makeSequenceArray(numArgs); synchronized(this) { if (argumentEvaluators == null) { allocateArgumentEvaluators(); } } for (int i = 0; i < numArgs; i++) { SequenceEvaluator eval = argumentEvaluators[i]; if (eval == null || (eval instanceof StreamingArgumentEvaluator && !streamed)) { eval = getArguments()[0].makeElaborator().eagerly(); } actualArgs[i] = eval.evaluate(c); } return actualArgs; } /** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. */ @Override public void export(ExpressionPresenter out) throws XPathException { out.startElement("ufCall", this); if (getFunctionName() != null) { out.emitAttribute("name", getFunctionName()); out.emitAttribute("tailCall", tailCall == NOT_TAIL_CALL ? "false" : tailCall == SELF_TAIL_CALL ? "self" : "foreign"); } out.emitAttribute("bSlot", "" + getBindingSlot()); for (Operand o : operands()) { o.getChildExpression().export(out); } if (getFunctionName() == null) { out.setChildRole("inline"); function.getBody().export(out); out.endElement(); } out.endElement(); } /** * Get a name identifying the kind of expression, in terms meaningful to a user. * * @return a name identifying the kind of expression, in terms meaningful to a user. * The name will always be in the form of a lexical XML QName, and should match the name used * in explain() output displaying the expression. */ @Override public String getExpressionName() { return "userFunctionCall"; } @Override public Object getProperty(String name) { if (name.equals("target")) { return function; } return super.getProperty(name); } @Override public StructuredQName getObjectName() { return getFunctionName(); } @Override public Elaborator getElaborator() { if (isTailCall()) { return new TailCallElaborator(); } else { return new UserFunctionCallElaborator(); } } private static class TailCallElaborator extends PullElaborator { public PullEvaluator elaborateForPull() { final UserFunctionCall expr = (UserFunctionCall) getExpression(); if (expr.bindingSlot >= 0) { return context -> { TailCallLoop.TailCallComponent info = new TailCallLoop.TailCallComponent(); Component target = expr.getTargetComponent(context); info.component = target; info.function = (UserFunction) target.getActor(); if (target.isHiddenAbstractComponent()) { throw new XPathException("Cannot call an abstract function (" + expr.getFunctionName().getDisplayName() + ") with no implementation", "XTDE3052"); } Sequence[] actualArgs = expr.evaluateArguments(context, false); ((XPathContextMajor) context).requestTailCall(info, actualArgs); return EmptyIterator.getInstance(); }; } else { TailCallLoop.TailCallFunction info = new TailCallLoop.TailCallFunction(); info.function = expr.getFunction(); return context -> { Sequence[] actualArgs = expr.evaluateArguments(context, false); ((XPathContextMajor) context).requestTailCall(info, actualArgs); return EmptyIterator.getInstance(); }; } } } private static class UserFunctionCallElaborator extends PullElaborator { private void testNotAbstract(UserFunctionCall expr, Component target) throws XPathException { if (target.isHiddenAbstractComponent()) { throw new XPathException("Cannot call an abstract function (" + expr.getFunctionName().getDisplayName() + ") with no implementation", "XTDE3052"); } } private XPathException.StackOverflow reportStackOverflow(Expression expr) { return new XPathException.StackOverflow("Too many nested function calls. May be due to infinite recursion", SaxonErrorCode.SXLM0001, expr.getLocation()); } /** * Get a function that evaluates the underlying expression in the form of * a {@link SequenceIterator} * * @return an evaluator for the expression that returns a {@link SequenceIterator} */ @Override public PullEvaluator elaborateForPull() { final UserFunctionCall expr = (UserFunctionCall) getExpression(); if (expr.bindingSlot >= 0) { // XSLT packages in general need dynamic binding return context -> { Component target = expr.getTargetComponent(context); testNotAbstract(expr, target); try { Sequence[] actualArgs = expr.evaluateArguments(context, false); UserFunction targetFunction = (UserFunction) target.getActor(); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); c2.setCurrentComponent(target); return targetFunction.call(c2, actualArgs).iterate(); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } }; } else { // Non-package case (XQuery) UserFunction targetFunction = expr.getFunction(); return context -> { try { Sequence[] actualArgs = expr.evaluateArguments(context, false); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); return targetFunction.call(c2, actualArgs).iterate(); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } }; } } /** * Get a function that evaluates the underlying expression in the form of * a {@link SequenceIterator} * * @return an evaluator for the expression that returns a {@link SequenceIterator} */ @Override public ItemEvaluator elaborateForItem() { final UserFunctionCall expr = (UserFunctionCall) getExpression(); if (expr.bindingSlot >= 0) { // XSLT packages in general need dynamic binding return context -> { Component target = expr.getTargetComponent(context); testNotAbstract(expr, target); try { Sequence[] actualArgs = expr.evaluateArguments(context, false); UserFunction targetFunction = (UserFunction) target.getActor(); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); c2.setCurrentComponent(target); return targetFunction.call(c2, actualArgs).head(); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } }; } else { // Non-package case (XQuery) UserFunction targetFunction = expr.getFunction(); return context -> { try { Sequence[] actualArgs = expr.evaluateArguments(context, false); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); return targetFunction.call(c2, actualArgs).head(); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } }; } } /** * Get a function that evaluates the underlying expression in push mode, by * writing events to an {@link Outputter} * * @return an evaluator for the expression in push mode */ @Override public PushEvaluator elaborateForPush() { final UserFunctionCall expr = (UserFunctionCall) getExpression(); if (expr.isTailCall()) { throw new AssertionError("Not using tail call path"); } // If the function call is evaluated in push mode, evaluate the function itself in push mode if (expr.bindingSlot >= 0) { // XSLT packages in general need dynamic binding return (output, context) -> { Component target = expr.getTargetComponent(context); testNotAbstract(expr, target); try { Sequence[] actualArgs = expr.evaluateArguments(context, false); UserFunction targetFunction = (UserFunction) target.getActor(); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); c2.setCurrentComponent(target); targetFunction.process(c2, actualArgs, output); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } return null; }; } else { // Non-package case (XQuery) UserFunction targetFunction = expr.getFunction(); return (output, context) -> { try { Sequence[] actualArgs = expr.evaluateArguments(context, false); XPathContextMajor c2 = targetFunction.makeNewContext(context, expr); targetFunction.process(c2, actualArgs, output); } catch (StackOverflowError err) { throw reportStackOverflow(expr); } return null; }; } } // /** // * Get a function that evaluates the underlying expression in the form of // * a {@link Item}. This must only be called for expressions whose result // * has cardinality zero or one. // * // * @return an evaluator for the expression that returns an {@link Item}. // */ // @Override // public ItemEvaluator elaborateForItem() { // // final UserFunctionCall expr = (UserFunctionCall) getExpression(); // // if (expr.bindingSlot >= 0) { // return context -> { // Component target = expr.getTargetComponent(context); // if (target.isHiddenAbstractComponent()) { // throw new XPathException("Cannot call an abstract function (" + // expr.getFunctionName().getDisplayName() + // ") with no implementation", "XTDE3052"); // } // return callTargetFunction(expr, target, context).head(); // }; // } else { // // Non-package case (XQuery) // UserFunction targetFunction = expr.getFunction(); // return context -> callTargetFunction(expr, targetFunction, context).head(); // } // // } @Override public UpdateEvaluator elaborateForUpdate() { final UserFunctionCall expr = (UserFunctionCall) getExpression(); UserFunction targetFunction = expr.getFunction(); // This is XQuery: target function is known return (context, pul) -> { Sequence[] actualArgs = expr.evaluateArguments(context, false); XPathContextMajor c2 = context.newCleanContext(); c2.setOrigin(expr); targetFunction.callUpdating(actualArgs, c2, pul); }; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy