Bug #3789
closedComponent reference variable is already bound, when compiling XSLT to SEF for Saxon-JS
100%
Description
I'm attaching the XSLT, problem seems to occur even when using command line with Saxon 9.8.0.12:
java -cp saxon9ee.jar;. net.sf.saxon.Transform -t -xsl:C:\path\component-reference-variable-already-bound.xsl -export:file.sef -target:JS -nogo
Exception in thread "main" java.lang.AssertionError: **** Component reference variable matchElement is already bound
at net.sf.saxon.expr.instruct.Actor.processComponentReference(Actor.java:146)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:109)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateBindingSlotsRecursive(Actor.java:112)
at net.sf.saxon.expr.instruct.Actor.allocateAllBindingSlots(Actor.java:103)
at net.sf.saxon.style.PrincipalStylesheetModule.compile(PrincipalStylesheetModule.java:1379)
at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:328)
at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:258)
at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:106)
at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:739)
at net.sf.saxon.Transform.doTransform(Transform.java:709)
at net.sf.saxon.Transform.main(Transform.java:81)
Files
Updated by Michael Kay over 6 years ago
- Category set to Internals
- Status changed from New to In Progress
- Assignee set to Michael Kay
- Priority changed from Low to Normal
I'm seeing this as the optimized expression (after function inlining):
bytecode(
let $vv:v1 := normalize-space($titleVal)
return (
let vv:v0 := bytecode(
convertUntyped(convert($matchElement = local-name($nodeName))))
return ValueOf(concat(
replace(lower-case(normalize-space($titleVal)), " ", "_", ""),
xs:untypedAtomic(string(
if (exists((($nodeName/preceding-sibling::node())
[bytecode(convertUntyped(convert($matchElement = local-name($nodeName))))])[bytecode(normalize-space(xs:string(atomizeSingleton(child::element(Q{}title)))) eq $vv:v1)]))
then ("42")))))))
It seems to have bound the variable $vv:v0 to the result of a subexpression, but then uses the original subexpression rather than the value; in consequence the subexpression appears twice and the variable reference $matchElement is therefore reached twice when walking the tree.
Updated by Michael Kay over 6 years ago
I thought it might have something to do with optimizing away the redundant conversion-to-string-and-back-to-boolean caused by using xsl:value-of rather than xsl:sequence to return the function result. But if I change the value-of to xsl:sequence it still fails in the same way, with the slightly simple expression
let $vv:v1 := normalize-space($titleVal)
return (let $vv:v0 := bytecode($matchElement = local-name($nodeName))
return ValueOf(concat(replace(lower-case(normalize-space($titleVal)), " ", "_", ""),
xs:untypedAtomic(string(if (exists((($nodeName/preceding-sibling::node())[bytecode($matchElement = local-name($nodeName))])
[bytecode(normalize-space(xs:string(atomizeSingleton(child::element(Q{}title)))) eq $vv:v1)])) then ("42"))))))
Updated by Michael Kay over 6 years ago
It's going wrong during loop-lifting, but running exp.verifyParentPointers() before entry to loop-lifting shows that there's already a bad parent pointer at that point.
I've added a call to verifyParentPointers() to Optimizer.trace() (why didn't I think of that before?)
I think the problem lies in OptimizerEE.tryIndexedFilter, which starts:
Expression k = tryToConvertFilterExpressionToKey(f, visitor, contextIsDoc);
if (k != null) {
return k;
}
if (!"EE".equals(visitor.getTargetEdition())) {
return f;
}
f.restoreParentPointers();
It's finding a filter expression that is suitable for indexing, and then abandons the optimization because it's compiling for Saxon-JS, which can't handle indexed expressions. But at this point f already contains invalid parent pointers (which is why the last line of this snippet is repairing them). We should either be repairing the parent pointers on the Saxon-JS path also; or (as far as I can see) we shouldn't even attempt the tryToConvertFilterExpressionToKey() on the Saxon-JS path if we're going to ignore the result.
Updated by Michael Kay over 6 years ago
The method tryToConvertFilterExpressionToKey finds that the expression E[P1][P2] isn't indexable, so it decides to have a try with E[P2]{P1]. It finds this isn't indexable either, but returns the expression in an inconsistent state, which the restoreParentPointers() call is designed to repair.
It should probably copy the expression before trying to change it especially if the change is experimental. Failing that, the repair should be done by the method tryToConvertFilterExpressionToKey itself rather than leaving it to the caller. But for a patch in 9.8, the least-risk solution seems to be to move the test for a non-EE target to the start of the method.
Updated by Michael Kay over 6 years ago
- Subject changed from Component reference variable is already bound when compiling XSLT to SEF to Component reference variable is already bound, when compiling XSLT to SEF for Saxon-JS
Updated by Michael Kay over 6 years ago
- Status changed from In Progress to Resolved
- Applies to branch 9.8, trunk added
- Fix Committed on Branch 9.8, trunk added
Updated by Radu Coravu over 6 years ago
Thanks for looking into this. Was this an issue which only manifested when doing XSLT->SEF or which could have thrown a similar error when doing XML+XSL?
Updated by Michael Kay over 6 years ago
The issue was specific to compiling a package with a target execution environment other than Saxon-EE.
Updated by Debbie Lockett over 6 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in Maintenance Release 9.8.0.14 added
Bug fix applied in the Saxon 9.8.0.14 maintenance release.
Please register to edit this issue