package client.net.sf.saxon.ce.expr;
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.ValueRepresentation;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.SequenceExtent;
import client.net.sf.saxon.ce.value.Value;
/**
* A TailCallLoop wraps the body of a function that contains tail-recursive function calls. On completion
* of the "real" body of the function it tests whether the function has executed a tail call, and if so,
* iterates to evaluate the tail call.
*/
public final class TailCallLoop extends UnaryExpression {
UserFunction containingFunction;
/**
* Constructor - create a TailCallLoop
* @param function the function in which this tail call loop appears
*/
public TailCallLoop(UserFunction function) {
super(function.getBody());
containingFunction = function;
}
/**
* Get the containing function
* @return the containing function
*/
public UserFunction getContainingFunction() {
return containingFunction;
}
/**
* Type-check the expression
*/
public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
operand = visitor.typeCheck(operand, contextItemType);
return this;
}
/**
* An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
* This method indicates which of these methods is provided. This implementation provides both iterate() and
* process() methods natively.
*/
public int getImplementationMethod() {
return operand.getImplementationMethod();
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
*/
private Expression copy() {
throw new UnsupportedOperationException("TailCallLoop.copy()");
}
/**
* Iterate over the sequence of values
*/
public SequenceIterator iterate(XPathContext context) throws XPathException {
final XPathContextMajor cm = (XPathContextMajor)context;
while (true) {
SequenceIterator iter = operand.iterate(cm);
ValueRepresentation extent = SequenceExtent.makeSequenceExtent(iter);
UserFunction fn = cm.getTailCallFunction();
if (fn == null) {
return Value.asIterator(extent);
}
if (fn != containingFunction) {
return Value.asIterator(tailCallDifferentFunction(fn, cm));
}
// otherwise, loop round to execute the tail call
}
}
/**
* Evaluate as an Item.
*/
public Item evaluateItem(XPathContext context) throws XPathException {
final XPathContextMajor cm = (XPathContextMajor)context;
while (true) {
Item item = operand.evaluateItem(context);
UserFunction fn = cm.getTailCallFunction();
if (fn == null) {
return item;
}
if (fn != containingFunction) {
return Value.asItem(tailCallDifferentFunction(fn, cm));
}
// otherwise, loop round to execute the tail call
}
}
/**
* Process the function body
* @param context The dynamic context, giving access to the current node,
* the current variables, etc.
*/
public void process(XPathContext context) throws XPathException {
final XPathContextMajor cm = (XPathContextMajor)context;
while (true) {
operand.process(context);
UserFunction fn = cm.getTailCallFunction();
if (fn == null) {
return;
}
if (fn != containingFunction) {
Value.asValue(tailCallDifferentFunction(fn, cm)).process(cm);
return;
}
// otherwise, loop round to execute the tail call
}
}
/**
* Make a tail call on a different function. This reuses the context object and the stack frame array
* where possible, but it does consume some Java stack space. It's still worth it, because we don't use
* as much stack as we would if we didn't return down to the TailCallLoop level.
* @param fn the function to be called
* @param cm the dynamic context
* @return the result of calling the other function
* @throws XPathException if the called function fails
*/
private ValueRepresentation tailCallDifferentFunction(UserFunction fn, XPathContextMajor cm) throws XPathException {
cm.resetStackFrameMap(fn.getStackFrameMap(), fn.getNumberOfArguments());
try {
return ExpressionTool.evaluate(fn.getBody(), fn.getEvaluationMode(), cm, 1);
} catch (XPathException err) {
err.maybeSetLocation(getSourceLocator());
err.maybeSetContext(cm);
throw err;
}
}
/**
* Determine the data type of the items returned by the expression
* @param th The type hierarchy cache
*/
public ItemType getItemType(TypeHierarchy th) {
return operand.getItemType(th);
}
}
// 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.