Bug #2770

Internal error evaluating template rule (IndexOutOfBoundsException)

Added by Michael Kay 10 months ago. Updated 9 months ago.

Start date:
Due date:
% Done:


Legacy ID:
Applies to branch:
9.7, 9.8
Fix Committed on Branch:
9.7, 9.8
Fixed in Maintenance Release:
Found in version:
Fixed in version:


Reported here by Vladimir Nestorovsky.

Looks like the component binding isn't working because it's using the wrong binding vector.


#2 Updated by Michael Kay 10 months ago

The function call that fails is in a template that has both a name and a match pattern. For component binding purposes, this is treated as two separate components, with different binding vectors. The expression trees for the two components should be disjoint, so that they can use different binding slot numbers. I suspect that the component hasn't been copied properly and that the two components are sharing some subtree of the expression tree, causing an incorrect slot number to be used.

#3 Updated by Michael Kay 10 months ago

  • Description updated (diff)

#4 Updated by Michael Kay 10 months ago

I was right: the template body contains a LocalParamSetter which contains a reference to a LocalParam. The LocalParam object isn't being copied, which means that the initializer of the LocalParam (written as the content of the xsl:param element but represented on the tree as a select attribute) isn't being copied, so the slot number allocated in its role as a named template gets overwritten by the slot number allocated in its role as a template rule. See LocalParamSetter.copy(), which doesn't copy the "binding" (i.e. the LocalParam).

Copying the LocalParam is tricky because we need to rebind all local variable references to it, throughout the template body. We can only do this once the whole template body has been copied. It's also complicated by the fact that the LocalParamSetter is used not only for initialization of template parameters, but also for the initialization of xsl:iterate parameters.

#5 Updated by Michael Kay 10 months ago

  • Fix Committed on Branch 9.7 added

After considering various alternatives, I have implemented a solution which works as follows: the Expression.copy() method has a new parameter "rebindings" which is a map of (oldBinding, newBinding) pairs. This list can be updated as the copy proceeds. LocalParam.copy() now creates a copy of itself, and adds the (old, new) pair to the binding list. When we reach a reference to the local parameter, if the binding is on the list then the copied local variable is bound to the new binding. (This mechanism can potentially be used to replace the current way in which let/for/some/every expressions rebind variable references, but I won't do that for 9.7 in the interests of stability.)

Currently applied to 9.7, tested against the supplied repro and regression tested against the XSLT3 test suite. Will now copy over to the 9.8 branch.

#6 Updated by Michael Kay 10 months ago

  • Status changed from New to Resolved
  • Applies to branch 9.8 added
  • Fix Committed on Branch 9.8 added
  • Fixed in Maintenance Release added

A patch has been committed on the 9.7 and 9.8 branches. Because the patch adds an argument to Expression.copy(), it affects a large number of files, but in nearly all cases the change is trivial.

#7 Updated by Michael Kay 10 months ago

  • Fixed in Maintenance Release added
  • Fixed in Maintenance Release deleted (

The addition of "fixed in" seems to have been finger-trouble. I can't find a way to clear all entries from the field: raised a question on the Redmine forum here:

The patch to LocalParam.copy() causes an NPE in test variable-0115. Committing a further (very simple) patch to fix this.

#8 Updated by O'Neil Delpratt 10 months ago

  • % Done changed from 0 to 100
  • Fixed in Maintenance Release deleted (

#9 Updated by O'Neil Delpratt 9 months ago

  • Status changed from Resolved to Closed
  • Fixed in Maintenance Release added

Bug fixed in maintenance release

Please register to edit this issue

Also available in: Atom PDF