Project

Profile

Help

Bug #5190

closed

xsl:iterate problem where the name of an xsl:iterate parameter duplicates the name of a local variable

Added by Martin Honnen over 2 years ago. Updated almost 2 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Category:
XSLT Conformance
Sprint/Milestone:
Start date:
2021-12-26
Due date:
% Done:

100%

Estimated time:
Applies to JS Branch:
2
Fix Committed on JS Branch:
2
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

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.

Actions #1

Updated by Martin Honnen over 2 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.

Actions #2

Updated by Michael Kay over 2 years ago

Thanks for reporting. Which compiler are you using?

Actions #3

Updated by Martin Honnen over 2 years ago

I run it directly the xslt3 command line tool of Saxon-JS, thus it is the compiler of Saxon-JS

Actions #4

Updated by Martin Honnen over 2 years ago

"XX compiler" is the name of the compiler (now that I could look it up in the Saxon-JS documentation online :)).

Actions #5

Updated by Michael Kay over 2 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.

Actions #6

Updated by Michael Kay over 2 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.

Actions #7

Updated by Michael Kay over 2 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.)

Actions #8

Updated by Michael Kay about 2 years ago

Added XSLT3 test case iterate-043.

Actions #9

Updated by Michael Kay about 2 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.

Actions #10

Updated by Michael Kay about 2 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.

Actions #11

Updated by Michael Kay about 2 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
Actions #12

Updated by John Lumley about 2 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 wrote:



Actions #13

Updated by Michael Kay about 2 years ago

  • Status changed from New to In Progress

(Current status: patch devised but not fully tested and therefore not committed).

Actions #14

Updated by Michael Kay about 2 years ago

  • Status changed from In Progress to Resolved
  • Fix Committed on JS Branch Trunk added

Fix now committed.

Actions #15

Updated by Michael Kay about 2 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.

Actions #16

Updated by Michael Kay about 2 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.

Actions #17

Updated by Michael Kay about 2 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.

Actions #18

Updated by Debbie Lockett almost 2 years ago

  • Fix Committed on JS Branch 2 added
  • Fix Committed on JS Branch deleted (Trunk)
Actions #19

Updated by Debbie Lockett almost 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