Bug #5190
closedxsl:iterate problem where the name of an xsl:iterate parameter duplicates the name of a local variable
100%
Description
I was experimenting with xsl:iterate
and found that the code
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="table">
<xsl:copy>
<xsl:iterate select="row">
<xsl:param name="row" as="element(row)?" select="()"/>
<xsl:on-completion select="$row"/>
<xsl:variable name="row" as="element(row)">
<xsl:apply-templates select="."/>
</xsl:variable>
<xsl:copy-of select="$row"/>
<xsl:next-iteration>
<xsl:with-param name="row" select="$row"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
</xsl:stylesheet>
when run with xslt3
from Saxon-JS 2.3 against a sample like
<table>
<row>row 1</row>
<row>row 2</row>
<row>row 3</row>
</table>
outputs "only" three rows
<?xml version="1.0" encoding="UTF-8"?>
<table>
<row>row 1</row>
<row>row 2</row>
<row>row 3</row>
</table>
<!--Run with Saxon-JS 2.3 Node.js-->
while Saxon HE Java 10.6 outputs the third row two times
<?xml version="1.0" encoding="UTF-8"?>
<table>
<row>row 1</row>
<row>row 2</row>
<row>row 3</row>
<row>row 3</row>
</table>
<!--Run with SAXON HE 10.6 -->
as does SaxonCS.
The spec for xsl:iterate
https://www.w3.org/TR/xslt-30/#iterate states that "The result of the xsl:iterate instruction is the concatenation of the sequences that result from the repeated evaluation of the contained sequence constructor, followed by the sequence that results from evaluating the xsl:break or xsl:on-completion element if any" so I think Saxon-JS has a bug here, failing to output the on-completion
result.
Updated by Martin Honnen almost 3 years ago
When I change the variable to use a different name than the parameter as in e.g.
<xsl:template match="table">
<xsl:copy>
<xsl:iterate select="row">
<xsl:param name="row" as="element(row)?" select="()"/>
<xsl:on-completion select="$row"/>
<xsl:variable name="new-row" as="element(row)">
<xsl:apply-templates select="."/>
</xsl:variable>
<xsl:copy-of select="$new-row"/>
<xsl:next-iteration>
<xsl:with-param name="row" select="$new-row"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
Saxon-JS manages to output the third row two times so the bug only seems to occur due to that use of the same name for variable and param.
Updated by Michael Kay almost 3 years ago
Thanks for reporting. Which compiler are you using?
Updated by Martin Honnen almost 3 years ago
I run it directly the xslt3
command line tool of Saxon-JS, thus it is the compiler of Saxon-JS
Updated by Martin Honnen almost 3 years ago
"XX compiler" is the name of the compiler (now that I could look it up in the Saxon-JS documentation online :)).
Updated by Michael Kay almost 3 years ago
It seems that the xsl:param
$row
is allocated slot 0, and the xsl:variable
$row
is allocated slot 1. The variable reference in xsl:on-completion
is correctly referencing slot 0, but the xsl:next-iteration/xsl:with-param
is binding the new value of the parameter to slot 1.
Updated by Michael Kay almost 3 years ago
I notice that in variables-and-params.xsl line 54 the code
<xsl:if test="parent::xsl:next-iteration">
<xsl:variable name="param" select="ancestor::xsl:iterate[1]/xsl:param[@name eq current()/@name]"/>
<!-- Typechecking performed in xpath phase -->
<xsl:sequence select="$param/(@as|@ex:asJ)"/>
</xsl:if>
looks questionable because it should be comparing the variable names as QNames. But this isn't the source of the problem. And perhaps it's not a problem anyway - variable names might have been turned into EQNames by this point in the processing.
Updated by Michael Kay almost 3 years ago
It looks to me as if the offending code is at xpath.xsl line 262:
<xsl:if test="self::ex:withParam">
<xsl:attribute name="slot" select="($local.variables(@name)[2], 0)[1]"/>
</xsl:if>
This is setting withParam/@slot
to the slot number allocated to the in-scope variable named $row
, which in this case is NOT the matching xsl:param
.
(Note, I don't think it makes sense to allocate a slot on withParam when it's an apply-templates
or call-template
instruction, because the parameters are then matched by name at run-time. But it probably does no harm.)
Updated by Michael Kay almost 3 years ago
It seems that at the point where we generate the withParam (xpath.xsl line262) the slot number allocated to the corresponding PARAMDEF is not available. The slot numbers aren't present in the XML tree used as input to this phase of processing, and although there's a tunnel parameter containing the mapping from in-scope variables to slot numbers, this doesn't include the relevant parameter because it has been shadowed by the local variable.
The options appear to be either (a) adding another tunnel parameter explicitly to contain the slot numbers of xsl:param
elements, or (b) fixing up the slot number of the withParam
in a subsequent processing phase.
Updated by Michael Kay almost 3 years ago
Taking the second option, the following fix appears to work. In component-bindings.xsl, add the rule
<!-- Bind xsl:next-iteration/xsl:with-param to the slot number of the corresponding xsl:iterate/xsl:param -->
<xsl:template match="ex:nextIteration/ex:withParam" mode="process-bindings">
<xsl:copy>
<xsl:sequence select="@*"/>
<xsl:attribute name="slot" select="ancestor::ex:iterate[1]/ex:params/ex:param[@name=current()/@name]/@slot"/>
<xsl:apply-templates select="*" mode="#current"/>
</xsl:copy>
</xsl:template>
This probably makes the existing code in xpath.xsl redundant.
Not fully tested as I don't have a fully working build environment on my current machine.
Updated by Michael Kay almost 3 years ago
- Subject changed from xsl:iterate problem where on-completion select="$param" doesn't return the parameter value as part of the xsl:iterate result to xsl:iterate problem where the name of an xsl:iterate parameter duplicates the name of a local variable
Updated by John Lumley almost 3 years ago
It never ceases to amaze me the number of ‘corner cases’ that ‘come out of the woodwork’. When I wrote the XX processing of xsl:iterate it never occurred to me we might have a duplication of local variable and parameter names……..
Sent from my iPad
On 27 Dec 2021, at 09:48, Saxonica Developer Community notifications@plan.io wrote:
Updated by Michael Kay almost 3 years ago
- Status changed from New to In Progress
(Current status: patch devised but not fully tested and therefore not committed).
Updated by Michael Kay almost 3 years ago
- Status changed from In Progress to Resolved
- Fix Committed on JS Branch Trunk added
Fix now committed.
Updated by Michael Kay almost 3 years ago
- Status changed from Resolved to In Progress
Marking this as resolved seems to have been premature. The test case iterate-043, which was added specifically for this bug, is still failing.
Updated by Michael Kay almost 3 years ago
The xsl:iterate instruction is not being processed during this phase of processing because of the rule (in component-bindings.xsl)
<xsl:template match="ex:co | ex:accumulator | ex:key" mode="process-bindings">
which takes a fast path by copying the template unchanged if it contains no references to external components. The conditions for this fast path need to be extended.
Updated by Michael Kay almost 3 years ago
- Status changed from In Progress to Resolved
Test now passing. An additional tweak was needed to make sure that the relevant components are actually processed in this phase.
Updated by Debbie Lockett over 2 years ago
- Fix Committed on JS Branch 2 added
- Fix Committed on JS Branch deleted (
Trunk)
Updated by Debbie Lockett over 2 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in JS Release set to SaxonJS 2.4
Bug fix applied in the SaxonJS 2.4 maintenance release.
Please register to edit this issue
Also available in: Atom PDF Tracking page