Bug #4802
closedOverzealous optimization
100%
Description
I believe I've discovered a subtle bug, in which an array:for-each fails to process correctly. In the code below there are four console messages labeled A, B, C, D. The console messages are intended to provide detailed insight into the content of the arrays/sub-arrays/maps.
The essense of the program is to read a set of HTML 'span' elements into a document-ordered array of maps; then to re-order the maps into an array of columns, each column as an array of maps; then to swap the row/column value for each map. These changes are reflected in the console output.
The sole difference between the two versions is the use of a saxon:timestamp() to force evaluation of the function.
In the "SwapNoTimestamp" function it appears that array:for-each is passing in the entire array of arrays each time it should be passing in a sub-array. In the "SwapWithTimestamp" function, it correctly passes in the sub-arrays.
Isolating and demonstrating this XPath behaviour was like debugging a black box. Perhaps a future blog post could address techniques for debugging XPath?
Saxon 10.0 plugin for Oxygen 22.1, compiling to Saxon-JS 2.0, executing in Firefox on Ubuntu.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
expand-text="yes"
extension-element-prefixes="ixsl saxon"
version="3.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:f="function"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
xmlns:js="http://saxonica.com/ns/globalJS"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:saxon="http://saxon.sf.net/"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.w3.org/1999/xhtml">
<xsl:template
name="xsl:initial-template">
<xsl:message>Entering initial-template</xsl:message>
<xsl:message>A▼</xsl:message>
<xsl:message>A▲ {f:getCells() => f:msg1()}</xsl:message>
<xsl:message>B▼</xsl:message>
<xsl:message>B▲ { f:getCells() => f:columnOrder() => f:msg2()}</xsl:message>
<xsl:message>C▼</xsl:message>
<xsl:message>C▲ { f:getCells() => f:columnOrder() => f:swapWithTimestamp() => f:msg2()
}</xsl:message>
<xsl:message>D▼</xsl:message>
<xsl:message>D▲ { f:getCells() => f:columnOrder() => f:swapNoTimestamp() => f:msg2() } </xsl:message>
</xsl:template>
<xsl:function
as="array(map(*))"
name="f:getCells">
<xsl:sequence
select="
array {
ixsl:page()/id('grid')//span ! map {
'val': ./text(),
'row': xs:integer(./count(preceding::span) idiv 3),
'col': xs:integer(./count(preceding::span) mod 3)
}
}" />
</xsl:function>
<xsl:function
as="array(array(map(*)))"
name="f:columnOrder">
<xsl:param
as="array(map(*))"
name="cells" />
<xsl:sequence
select="
array {
for $n in 0 to 2
return
array:filter($cells, function ($c) {
xs:integer(map:find($c, 'col')) = xs:integer($n)
})
}" />
</xsl:function>
<xsl:function
name="f:swapWithTimestamp">
<xsl:param
as="array(array(map(*))*)"
name="cells" />
<xsl:message>SwapWithTimestamp {saxon:timestamp()}: {array:size($cells)}</xsl:message>
<xsl:sequence
select="
array:for-each($cells, function ($c) {
f:swapRowCol($c, array {})
})" />
</xsl:function>
<xsl:function
name="f:swapNoTimestamp">
<xsl:param
as="array(array(map(*))*)"
name="cells" />
<xsl:message>SwapNoTimestamp: {array:size($cells)}</xsl:message>
<xsl:sequence
select="
array:for-each($cells, function ($c) {
f:swapRowCol($c, array {})
})" />
</xsl:function>
<xsl:function
as="array(map(*))"
name="f:swapRowCol">
<xsl:param
as="array(map(*))"
name="cells" />
<xsl:param
as="array(map(*))"
name="acc" />
<xsl:message>Swapping: {array:size($cells)} cells remaining</xsl:message>
<xsl:sequence
select="
if (array:size($cells) gt 0) then
let $h := array:head($cells)
return
f:swapRowCol(array:tail($cells), array:append($acc, map {
'val': $h('val'),
'row': $h('col'),
'col': $h('row')
}))
else
$acc" />
</xsl:function>
<xsl:function
name="f:msg1">
<xsl:param
as="array(map(*))"
name="cells" />
<xsl:message xml:space="preserve">{array:size($cells)} cells in the array:{array:for-each($cells,function($c){' val: '||$c('val')||' row: '||$c('row')||' col: '||$c('col')})}</xsl:message>
</xsl:function>
<xsl:function
name="f:msg2">
<xsl:param
as="array(array(map(*)*))"
name="cols" />
<xsl:message xml:space="preserve">{array:size($cols)} columns in the array:{array:for-each($cols,function($c){' '||array:size($c)||' cells in column'||f:msg1($c)})}</xsl:message>
</xsl:function>
</xsl:stylesheet>
<!doctype html>
<html
lang="en">
<head>
<meta
charset="utf-8">
<title>Test</title>
<script src="js/Saxon-JS-2.0/SaxonJS2.rt.js"></script>
<script type="text/javascript">
window.onload = function () {
SaxonJS.transform({
stylesheetLocation: "xsl/webapp.sef.json",
initialTemplate: "Q{http://www.w3.org/1999/XSL/Transform}initial-template",
});
};</script>
</head>
<body>
<section>
<h4>Data Source</h4>
<div
id="grid">
<div><span>1</span><span>2</span><span>3</span></div>
<div><span>4</span><span>5</span><span>6</span></div>
<div><span>7</span><span>8</span><span>9</span></div>
</div>
</section>
</body>
</html>
Files
Please register to edit this issue
Also available in: Atom PDF Tracking page