Project

Profile

Help

Bug #3723

closed

Problem with reflexive extension function using ?void=this and SEF files

Added by Michael Kay about 6 years ago. Updated about 6 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
-
Sprint/Milestone:
-
Start date:
2018-03-18
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

Reported by direct email to MHK.

The essence is a call to a user-defined function which is returning an external Java object:

<xsl:function as="jt:java.util.zip.ZipOutputStream" name="test:zos">
	<xsl:param as="xs:string" name="path" />
        ....
	<xsl:sequence select="ZipOutputStream:new($fos)" />
</xsl:function>

where the external object then has an extension function applied to it:

<xsl:variable name="zos"
	select="Q{java:java.util.zip.ZipOutputStream?void=this}setComment(test:zos(xxxx), 'comment')" />

This is working fine when invoked directly, but fails when running from a SEF file, with the error:

java.lang.IllegalStateException: A Closure can only be read once
	at net.sf.saxon.value.Closure.iterate(Closure.java:166)
	at com.saxonica.expr.JavaExtensionFunctionCall.iterate(JavaExtensionFunctionCall.java:438)

The user-defined-function test:zos() returns a Closure that can only be used once because it thinks the result will only be needed once (it is used as the argument to the setComment() call). However, because of the ?void=this option in the extension function URI, the value is actually used twice: once as the argument to the call, and again as the result of the call.

Actions #1

Updated by Michael Kay about 6 years ago

  • Subject changed from Problem with reflexive extension functions and SEF files to Problem with reflexive extension function using ?void=this and SEF files
  • Description updated (diff)
  • Status changed from New to In Progress
Actions #2

Updated by Michael Kay about 6 years ago

  • Description updated (diff)
Actions #3

Updated by Michael Kay about 6 years ago

So we've established why it fails in the SEF case.

Why doesn't it fail when executed directly?

The answer is that the user-defined-function test-zos() is being evaluated eagerly because it is known to return a singleton. (Saxon uses a heuristic here that lazy evaluation delivers no benefits for a function that returns a singleton. This isn't always true, but it's a reasonable guess, and whether or not it's the correct strategy from a performance perspective is irrelevant to this bug.)

So it seems there are two separate issues here.

Firstly, when we invoke a function with ?void=this we should make sure that any Closure supplied as the argument is materialized, so that we only evaluate it once.

Secondly, we should look at why the execution strategy for lazy evaluation with a SEF file ends up being different from the original strategy. Have we lost some type information that is used for run-time decision-making?

Actions #4

Updated by Michael Kay about 6 years ago

We can solve the first problem by adding the line

argValues[0] = SequenceTool.makeRepeatable(argValues[0]);

before

theInstance = getTargetInstance(argValues[0], context);
// (JavaExtensionFunctionCall line 540)
Actions #5

Updated by Michael Kay about 6 years ago

It seems the difference between the .xsl and .sef paths is that on the .sef path, the staticType field of the UserFunctionCall object is null. This has the consequence that the cardinality of the result is unknown, which means that lazy evaluation is used.

The static type is computed during the type checking phase. It is not exported into the SEF file, and no attempt is made to recompute the value when the SEF is reloaded.

This can be fixed by adding the line

((UserFunctionCall) call).setStaticType(((UserFunction) c.getActor()).getResultType());

into the PackageLoader's fixup phase.

Although the UserFunctionCall expression is a little unusual in retaining the static type as an explicit property of the expression, there are other expressions that retain similar derived properties for use at run-time and the problem might be more general.

Actions #6

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

The above fixes are sufficient to fix the problem.

There's room for improvement, however. Unlike calls to user functions, calls to Java extension functions are deciding the evaluation mode for each argument on each function call execution, rather than computing the evaluation modes once and reusing them (and saving the evaluation modes in the SEF file). After reloading from a SEF, this requires recomputing of type and dependency information that is not normally used at run-time and is therefore not saved in the SEF. I will look at improving this on the development branch.

Fix committed for 9.8 and trunk.

Actions #7

Updated by O'Neil Delpratt about 6 years ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 9.8.0.11 added

Bug fix applied in the Saxon 9.8.0.11 maintenance release.

Please register to edit this issue

Also available in: Atom PDF