Project

Profile

Help

Bug #3789

closed

Component reference variable is already bound, when compiling XSLT to SEF for Saxon-JS

Added by Radu Coravu almost 6 years ago. Updated over 5 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Internals
Sprint/Milestone:
-
Start date:
2018-05-17
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
9.8, trunk
Fix Committed on Branch:
9.8, trunk
Fixed in Maintenance Release:
Platforms:

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

Actions #1

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

Actions #2

Updated by Michael Kay almost 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"))))))
Actions #3

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

Actions #4

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

Actions #5

Updated by Michael Kay almost 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
Actions #6

Updated by Michael Kay almost 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
Actions #7

Updated by Radu Coravu almost 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?

Actions #8

Updated by Michael Kay almost 6 years ago

The issue was specific to compiling a package with a target execution environment other than Saxon-EE.

Actions #9

Updated by Radu Coravu almost 6 years ago

Cool, thanks.

Actions #10

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

Also available in: Atom PDF