#331 Recursive call in function arguments

v8.3
closed
Michael Kay
5
2015-01-13
2005-03-21
Michael Kay
No

Saxon 8.3 generally uses lazy evaluation for function
calls.

If a recursive function call appears within the
arguments of a function, for example

my:f ($x[1], my:f($x[position()>1] )

this means that the recursive call is made from the
callee's stack frame rather than the caller's stack
frame. If this occurs repeatedly it can greatly
increase the number of stack frames required, causing
the stack to overflow.

The problem can be circumvented by assigning a variable
to the expression that performs the recursive call, and
using the variable in the function call.

The source clearance is as follows:

(a) in net.sf.saxon.expr.StaticProperty

(a1) define a new item

public static final int DEPENDS_ON_USER_FUNCTIONS =

1<< 15;

(a2) include this in the definition of DEPENDENCY_MASK:

public static final int DEPENDENCY_MASK =
        DEPENDS_ON_CONTEXT_DOCUMENT |
        DEPENDS_ON_CONTEXT_ITEM |
        DEPENDS_ON_CURRENT_GROUP |
        DEPENDS_ON_REGEX_GROUP |
        DEPENDS_ON_CURRENT_ITEM |
        DEPENDS_ON_FOCUS |
        DEPENDS_ON_LOCAL_VARIABLES |
        DEPENDS_ON_USER_FUNCTIONS;

(b) In net.sf.saxon.expr.UserFunctionCall:

(b1) add the method:

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

(b2) in the method callFunction(), change the for loop
to read:

    for (int i=0; i<numArgs; i++) {
        // Decide what form of lazy evaluation to

use based on the number of references to the argument
int refs =
function.getParameterDefinitions()[i].getReferenceCount();
if (argument[i] instanceof Value) {
actualArgs[i] = (Value)argument[i];
} else {
if (refs == 0) {
actualArgs[i] =
EmptySequence.getInstance();
} else if
((argument[i].getDependencies() &
StaticProperty.DEPENDS_ON_USER_FUNCTIONS) != 0) {
actualArgs[i] =
ExpressionTool.eagerEvaluate(argument[i], c);
} else {
boolean keep = (refs > 1);
actualArgs[i] =
ExpressionTool.lazyEvaluate(argument[i], c, keep);
}
}

This change has not yet been fully regression tested: I
would advise using the circumvention (i.e. using an
extra variable) if you can.

Michael Kay

Discussion