Menu

#329 xsl:call-template moved out of for-each loop

v8.3
closed
5
2012-10-08
2005-03-17
Michael Kay
No

An xsl:call-template instruction may be incorrectly
moved out of an xsl:for-each loop by the expression
optimizer, meaning that (a) it is only executed once
instead of multiple times, and (b) it is executed with
the wrong context node.

The problem occurs when the xsl:call-template is inside
an xsl:if or xsl:choose within the xsl:for-each, and no
expressions within the xsl:if (such as the test
expression or the parameters to the call-template) are
explicitly dependent on the context.

There are actually two separate errors in the code.
There are two reasons that should stop an
xsl:call-template being moved out of the for-each loop:
firstly, it creates new nodes, and secondly, it's
likely that the called template is dependent on the
context. When both the bugs combine, neither of these
mechanisms is activated, and so the move takes place.

The fact that the call-template instruction depends on
the context isn't being registered. This can be fixed
by adding the following method to
net.sf.saxon.instruct.CallTemplate:

public int getIntrinsicDependencies() {
    // we could go to the called template and find

which parts of the context it depends on, but this
// would create the risk of infinite recursion.
So we just assume the worst
return StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
StaticProperty.DEPENDS_ON_FOCUS;
}

The fact that the call-template instruction typically
creates new nodes is being masked by an error in the
logic for the xsl:if instruction. The method
createsNewNodes() in class net.sf.saxon.instruct.Choose
should be changed to read:

public final boolean createsNewNodes() {
    for (int i=0; i<actions.length; i++) {
        int props = actions[i].getSpecialProperties();
        if ((props & StaticProperty.NON_CREATIVE)

== 0) {
return true;
};
}
return false;
}

(the 8.3 code has (int i=1) in the for loop)

Michael Kay

Discussion