Bug #5490
closedSaxon-JS 2.4 fails to run Schxslt pipeline XSLT that tries to first compile Schematron schema to XSLT and then to apply that schema to validate an XML instance document: TypeError: Cannot read property 'childNodes' of undefined
100%
Description
I have found a major regression with Saxon 2.4, I have a stylesheet that runs two XSLT transformations in a pipeline, the first step uses Schxslt 1.8.6 to compile a Schematron schema to XSLT, the second step applies the generated XSLT against an input XML document to validate that input XML against the Schematron schema and to generate an SVRL report. That worked fine for me with Saxon-JS 2.2 and 2.3 but in 2.4 fails with an error:
C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest>xslt3 -t -xsl:run-pipeline-for-svrl-and-apply-to-schema.sef.json -s:sample1.xml schema-uri=schematron-schema1.sch
SaxonJS 2.4 from Saxonica
Node.js version v14.16.0
SEF generated by SaxonJS 2.4 at 2022-05-14T12:47:24.253+02:00
Transformation failure: TypeError: Cannot read property 'childNodes' of undefined
Cannot read property 'childNodes' of undefined
When I run the code from Node.js I get a longer stack trace included that hopefully helps:
C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest>node app.js
C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\saxon-js\SaxonJS2N.js:4224
push:function(h,M,d){if("undefined"===typeof d)throw ob.fb(Error().stack),new L("No output for "+m(h));if(u[h.N]){var c=e(h);try{c(M,d)}catch(n){throw n instanceof L&&!n.xsltLineNr&&nb(n,h),n;}}else{c=Wd.elaborate(h);try{for(var l=c(M),q;null!=(q=l.next());)d.append(q)}catch(n){throw n instanceof L&&!n.xsltLineNr&&nb(n,h),n;}}},bj:g,Of:t,mh:r}}();var Ve=function(){function a(Q,ja,ca,z,A){function F(E){return null===n.Ma(E)}function I(E){return E.nodeType===B.nodeType&&(null===R?!0:n.ba(E).equals(R))}var S=A.fixed.evaluator;if(ja){var B=S.evaluate(ja,A).next();if(!B||!n.Q(B))throw new L("xsl:number/@select must yield a single node","XTTE1000");}else A.failIfNoCurrentFocus("XTTE0990","xsl:number"),B=A.getCurrentFocus();var R=n.ba(B),J=I;if(null!==ca){var fa=Ae.pd(ca);J=function(E){return Ae.matches(fa,E,A)}}if(null===z)ja=F;else{var ka=Ae.pd(z);
^
TypeError: Cannot read property 'childNodes' of undefined
at transform (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4408:426)
at C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4463:173
at C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4477:57
at Object.push (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4224:232)
at e (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4816:245)
at t (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4820:320)
at Object.transform (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\node_modules\←[4msaxon-js←[24m\SaxonJS2N.js:4840:413)
at Object.<anonymous> (C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest\app.js:16:26)
←[90m at Module._compile (internal/modules/cjs/loader.js:1063:30)←[39m
←[90m at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)←[39m
←[90m at Module.load (internal/modules/cjs/loader.js:928:32)←[39m
←[90m at Function.Module._load (internal/modules/cjs/loader.js:769:14)←[39m
←[90m at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)←[39m
←[90m at internal/main/run_main_module.js:17:47←[39m
I put the files in a Github repository: https://github.com/martin-honnen/SaxonJSSchxsltPipelineTest.
Updated by Martin Honnen over 2 years ago
I have also created the branch https://github.com/martin-honnen/SaxonJSSchxsltPipelineTest/tree/UseSaxonJS23/SaxonJSSchxsltPipelineTest that uses Saxon-JS 2.3 and where the code works fine and outputs an SVRL report instead of giving that error:
C:\Users\marti\source\repos\SaxonJSSchxsltPipelineTest\SaxonJSSchxsltPipelineTest>node app.js
<?xml version="1.0" encoding="UTF-8"?>
<svrl:schematron-output xmlns:error="https://doi.org/10.5281/zenodo.1495494#error" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns:schxslt-api="https://doi.org/10.5281/zenodo.1495494#api" xmlns:schxslt="https://doi.org/10.5281/zenodo.1495494" xmlns:svrl="http://purl.oclc.org/dsdl/svrl" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<svrl:metadata xmlns:dct="http://purl.org/dc/terms/" xmlns:skos="http://www.w3.org/2004/02/skos/core#">
<dct:creator>
<dct:Agent>
<skos:prefLabel>Saxon-JS/2.3</skos:prefLabel>
</dct:Agent>
</dct:creator>
<dct:created>2022-05-14T13:24:31.928+02:00</dct:created>
<dct:source>
<rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
<dct:creator>
<dct:Agent>
<skos:prefLabel>SchXslt/1.8.6 Saxon-JS/2.3</skos:prefLabel>
<schxslt.compile.typed-variables xmlns="https://doi.org/10.5281/zenodo.1495494#">true</schxslt.compile.typed-variables>
</dct:Agent>
</dct:creator>
<dct:created>2022-05-14T13:24:30.896+02:00</dct:created>
</rdf:Description>
</dct:source>
</svrl:metadata>
<svrl:active-pattern documents="file:///C:/Users/marti/source/repos/SaxonJSSchxsltPipelineTest/SaxonJSSchxsltPipelineTest/run-pipeline-for-svrl-and-apply-to-schema.sef.json"/>
<svrl:fired-rule context="root"/>
<svrl:failed-assert location="/Q{}root[1]" test="@*">
<svrl:text>root has no attributes.</svrl:text>
</svrl:failed-assert>
<svrl:successful-report location="/Q{}root[1]" test=". = 'This is an example.'">
<svrl:text>root element has value 'This is an example.'</svrl:text>
</svrl:successful-report>
</svrl:schematron-output>
Updated by Martin Honnen over 2 years ago
To see whether the problem only occurs in the context of (a relatively complex code like) Schematron compilation I have now also added some files identity.xsl
, identity.sef.json
, run-and-apply-identity.js
, run-and-apply-identity.xsl
, run-and-apply-identity.sef.json
and if you run node run-and-apply-identity.js
you get the same error with Saxon-JS 2.4 as with the more complex Schematron samples while Saxon-JS 2.3 runs the identity transformation fine.
Updated by Norm Tovey-Walsh over 2 years ago
Well, that is disappointing. I shall investigate and see if we need to do a 2.5.
Updated by Martin Honnen over 2 years ago
I think the regression is due to the fix of https://saxonica.plan.io/issues/5039, previously the Saxon-JS code created always a document node, now it might create a document fragment node but the code processing a stylesheet expects a document nodes as at some point it wants to process all top level nodes by processing e.g. stylesheet.documentElement.childNodes, only now, with 2.4, stylesheet can be a document fragment node and document fragment nodes do not have a documentElement at all. That way the attempt to process a stylesheet that is formed as a fragment fails with the attempt to access childNodes
on the undefined documentElement
of the stylesheet.
Updated by Martin Honnen over 2 years ago
If I change a line doing
for (ma = Aa(ha.documentElement.childNodes),
y = ma.next(); !y.done; y = ma.next())
to
for (ma = Aa(ha.documentElement ? ha.documentElement.childNodes : ha.childNodes),
y = ma.next(); !y.done; y = ma.next())
the Saxon-JS 2.4 code seems to be able to process the stylesheet fine.
I haven't checked whether there is other stylesheet processing code around that tries to process stylesheet.documentElement.childNodes.
Updated by Martin Honnen over 2 years ago
I think the fix should rather be
for (ma = Aa(ha.documentElement ? ha.documentElement.childNodes : ha.firstElementChild.childNodes),
y = ma.next(); !y.done; y = ma.next())
Updated by Norm Tovey-Walsh over 2 years ago
- Status changed from New to In Progress
That does appear to be the only place where documentNode.childNodes
is called. I persuaded myself when I was working on the fix that stylesheets wouldln't be a problem because they would always be documents.
I'm torn about the best solution. Your proposal fixes the problem by changing the code so that a single-rooted document fragment will be processed correctly. But if there's a code path that allows a document fragment to reach there, then the possibility exists that a non-single-rooted fragment might be passed in. In that case, just treating the children of the first child as the content is probably wrong.
Where we previously always attempted to coerce inputs into documents (failing if a fragment couldn't be), I wonder if what we should do instead is check the fragment and, if it is single rooted, coerce it into a document. Otherwise, leave it as a fragment.
I shall have to experiment a bit.
Updated by Martin Honnen over 2 years ago
I just tried the following XQuery with Saxon CS 11.3 and Saxon HE 11.3:
transform(map {
'stylesheet-node' : document { <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/">
<xsl:next-match/>
<xsl:comment>Fragment 1</xsl:comment>
</xsl:template>
</xsl:stylesheet>,
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/">
<xsl:next-match/>
<xsl:comment>Fragment 2</xsl:comment>
</xsl:template>
</xsl:stylesheet>
},
'source-node' : document { <root>test</root> }
})?output
Result: <?xml version="1.0" encoding="UTF-8"?><root>test</root><!--Fragment 1-->
No idea whether that is coincidence or just an accident or quirk but for some reason my suggested fix seems to make Saxon-JS treat a fragment with several top level elements like Saxon Java or CS seem to do, they process the first child element of the fragment as the XSLT stylesheet.
Perhaps it is better to ask Mike what he thinks how a node that is an XDM document node having more than one child element should be treated if used as the stylesheet-node
of fn:transform
.
Updated by Norm Tovey-Walsh over 2 years ago
- Status changed from In Progress to Resolved
I've pushed a fix for this. My fix is (currently) to force the stylesheetNode
to be a document.
Updated by Michael Kay over 2 years ago
Looking at this, I'm wondering what happens if stylesheetNode
is the xsl:stylesheet
element node?
My reading of the specs (both the XSLT 3.0 spec, §3.6, and the fn:transform
spec, is that we should allow a document node containing a single element node, or an element node, but not a document node with multiple element children.
Updated by Martin Honnen over 2 years ago
I think both Saxon-JS 2.3 and 2.4 fail on an element node for stylesheet-node
in fn:transform
when trying to convert the SEF XML to SEF JSON, instead of having the root element of the stylesheet package they process a text node and try to access the .attributes.length
of the text node (which should be an element node) and then fail with an error that length
of undefined can't be read.
Updated by Martin Honnen over 2 years ago
https://github.com/w3c/qt3tests/blob/master/fn/transform.xml doesn't seem to have a test case where an xsl:stylesheet
element node instead of a document node is passed to fn:transform
.
Updated by Michael Kay over 2 years ago
Added test case fn-transform-7a to pass an xsl:stylesheet element. It passes in SaxonJ 10.x (and therefore presumably 11.x).
Also added -7b and -7c to pass a simplified stylesheet, both as a document node and as an element node.
Also added -7d and -7e to check that the xsl:stylesheet element can be the second child of a document fragment node.
Updated by Debbie Lockett about 2 years ago
- Sprint/Milestone changed from SaxonJS 2.4 to SaxonJS 2.5
- Applies to JS Branch Trunk added
- Fix Committed on JS Branch 2, Trunk added
Updated by Norm Tovey-Walsh about 2 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in JS Release set to SaxonJS 2.5
Fixed in SaxonJS 2.5.
Please register to edit this issue
Also available in: Atom PDF Tracking page