Local variables that are accessible from a given syntax tree node will
first be put on the JVM's stack and stored in a local variable slot. This
makes the variable or parameter accessible from all code within that
method. But, in some special cases, the code that is compiled to handle an
element/expression within the variable scope is not put inside the same
method as the actual variable. This is the case for some predicates.
All syntax-tree nodes implement the isClosureBoundary()
method
to indicate if its child an ancestor nodes will end up in a different method
then itself. This method is used by the Variable
and
Param
classes to determine if the variable or parameter will
"escape" the variable frame.
| | |
|
<xsl:for-each select="/foo/bar/baz">
<xsl:variable name="pos" select="3"/>
<xsl:apply-templates select="/foo/bar[$pos]"/>
</xsl:for-each> | |
| | |
The predicate in this stylesheet fragment is compiled into a separate
auxiliary class that implements the Filter
interface. It will
therefore not have access to the variable "pos" in the current stack frame.
A common technique for cases like this is to use a "closure". A
closure is a record that contains references to all variables that are in
scope for a certain part of the compiled scope. This is done in a very
simple manner in XSLTC. All variables or parameters that can "escape" the
stack are passed to the translet via its addVariable()
method.
They can then later be retrieved by the getVariable()
method.
Important note 1: A predicate does not always result in a
auxiliary class. In some cases we optimize the code by using tailored
iterators and goodies like that instead. We may want to update the
predicate code to check if an auxiliary class will be generated before
returning true or false from the isClosureBoundary()
method.
Important note 2: There could be other closure boundaries
that we have not yet discovered or considered. This could be, for instance,
sort records and other auxiliary classes:
| | |
|
<xsl:variable name="sort-order" select="'decending'"/>
<xsl:for-each select="/foo/bar/baz">
<xsl:sort select="@name" order="$sort-order"/>
<xsl:value-of select="."/>
</xsl:for-each> | |
| | |
I would not be surprised if this fails. A fix could be to implement the
isClosureBoundary()
in the Sort
class and have the
method return 'true' in all cases.