Project

Profile

Help

Bug #2554

closed

ArrayIndexOutOfBoundsException while evaluating JAXP XPath expression with a variable reference

Added by Benjamin Graf over 8 years ago. Updated about 8 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
JAXP Java API
Sprint/Milestone:
-
Start date:
2015-12-20
Due date:
% Done:

100%

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

Description

Failure exists since latest version 9.7. See test case in attachement

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1

at net.sf.saxon.value.Closure.saveContext(Closure.java:121)

at net.sf.saxon.value.Closure.make(Closure.java:97)

at net.sf.saxon.expr.parser.ExpressionTool.evaluate(ExpressionTool.java:280)

at net.sf.saxon.expr.parser.ExpressionTool.lazyEvaluate(ExpressionTool.java:418)

at net.sf.saxon.expr.FunctionCall.evaluateArguments(FunctionCall.java:558)

at net.sf.saxon.expr.FunctionCall.iterate(FunctionCall.java:544)

at net.sf.saxon.expr.Expression.effectiveBooleanValue(Expression.java:828)

at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:202)

at test.test.Main.main(Main.java:36)

Files

Test.zip (7.95 KB) Test.zip Benjamin Graf, 2015-12-20 17:29
Actions #1

Updated by Michael Kay over 8 years ago

  • Status changed from New to In Progress
  • Assignee set to Michael Kay

Reproduced as JUnit test jaxptest/XPathTest/testBug2554. Simplified to:

            String xml = "<root><test name=\"test\"/></root>";
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = factory.newDocumentBuilder();
            Document doc = db.parse(new InputSource(new StringReader(xml)));
            XPathFactory factory2 = XPathFactory.newInstance(
                    XPathFactory.DEFAULT_OBJECT_MODEL_URI,
                    "net.sf.saxon.xpath.XPathFactoryImpl",
                    ClassLoader.getSystemClassLoader());
            XPath xPath = factory2.newXPath();
            final Map<String, String> variableMappings = Collections.singletonMap("name", "test");
            xPath.setXPathVariableResolver(new XPathVariableResolver() {

                @Override
                public Object resolveVariable(QName varName) {
                    return variableMappings.get(varName.getLocalPart());
                }
            });
            XPathExpression expr = xPath.compile("boolean(/root/test[@name=$name])");
            expr.evaluate(doc, XPathConstants.BOOLEAN);
Actions #2

Updated by Michael Kay over 8 years ago

Tricky. The Closure class expects all local variables to have a slot number allocated, but with the JAXP XPath variable resolver mechanism this is not the case. (This API doesn't provide any information about variables at compile time, so it's very different from all other paths.)

Actions #3

Updated by Michael Kay over 8 years ago

I started down the route of "how come this worked in 9.6". Well, it seems that in 9.6 the class JAXPVariableReference extends LocalVariableReference, but it binds to a JAXPVariable, which implements LocalBinding but returns isGlobal()=true. So in 9.6 I think it's working because a JAXP variable has some judicious mix of local-variable and global-variable behaviour. In other words, I think it's working by accident rather than by design. So I think that trying to make it work like it did in 9.6 is not going to be a useful way forward: we're going to have to look at it from first principles.

It's also worth noting that the HE and EE behaviour is different. It fails on both, for similar reasons, but in significantly different ways. On the HE path data($name) is implemented as a MemoClosure, on the EE path it becomes an IndexedValue which is evaluated eagerly.

Actions #4

Updated by Michael Kay over 8 years ago

I've just noticed that in the Javadoc for XPath.compile(), it says:

If expression contains any variables, the XPathVariableResolver in effect at compile time will be used to resolve them.

I have checked the original JSR 206 (JAXP 1.3) dating from 2004 and this rule was not originally present - it has been added since (JAXP has a nasty history of changing the spec without advertising the fact). The original spec said that functions were resolved at compile time, but it did not say the same for variables. The JDK 1.5 documentation does not include this rule:

https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPath.html#compile(java.lang.String)

but the Java 6 documentation does:

http://docs.oracle.com/javase/6/docs/api/javax/xml/xpath/XPath.html

This rule actually makes many of the complexities of the Saxon design redundant; if the values of variables are known at compile time, they can be statically substituted into the expression.

We have to think about whether enforcing this rule at this stage in the game will create any incompatibility issues for existing users. It could, but our general approach to this is that spec conformance takes priority over backwards compatibility.

Actions #5

Updated by Michael Kay over 8 years ago

No, I think that's a misinterpretation of this JAXP rule. The new rule introduced in Java 6 means that after calling XPath.compile(), a subsequent call on XPath.setXPathVariableResolver() should have no effect. But it doesn't say that a change to the state of the existing XPathVariableResolver has no effect. In fact, the specification of XPathVariableResolver explicitly says:

Although variables may be mutable, that is, an application may wish to evaluate the same XPath expression more than once with different variable values, in the course of evaluating any single XPath expression, a variable's value must not change.

The implication there is that it's the state of the XPathVariableResolver at run time that matters, which is Saxon's current interpretation.

Actions #6

Updated by Michael Kay over 8 years ago

  • Subject changed from Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1 while evaluating simple XPath to ArrayIndexOutOfBoundsException while evaluating JAXP XPath expression with a variable reference
  • Status changed from In Progress to Resolved
  • Priority changed from High to Normal
  • Fix Committed on Branch 9.7 added

I have implemented a fairly radical fix, which greatly simplifies the design. During compilation, if the static context is a JAXPStaticContext, the variable reference is still compiled to a JAXPVariableReference object. However, JAXPVariableReference is no longer treated as a local or global variable reference, instead it is something much more like the XSLT system-property() function call: it's simply an custom expression which, when evaluated, asks the variable resolver for the value of the variable. This means that it now completely bypasses the mechanisms for storing values of local variables (on the stack) or global variables (in the bindery).

Actions #7

Updated by O'Neil Delpratt about 8 years ago

  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 9.7.0.2 added

Bug fix applied in the Saxon 9.7.0.2 maintenance release

Actions #8

Updated by O'Neil Delpratt about 8 years ago

  • Status changed from Resolved to Closed

Please register to edit this issue

Also available in: Atom PDF