• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eXist-db / exist / 40387836984

26 Oct 2026 07:33PM UTC coverage: 58.173% (+0.005%) from 58.168%
40387836984

push

github

web-flow
Merge pull request #6731 from line-o/bugfix/binary-value-frame-ownership

[bugfix] Release a binary value with the scope that created it

32491 of 61752 branches covered (52.62%)

Branch coverage included in aggregate %.

92 of 129 new or added lines in 8 files covered. (71.32%)

27 existing lines in 6 files now uncovered.

85598 of 141244 relevant lines covered (60.6%)

2.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.43
/exist-core/src/main/java/org/exist/xquery/value/FunctionReference.java
1
/*
2
 * eXist-db Open Source Native XML Database
3
 * Copyright (C) 2001 The eXist-db Authors
4
 *
5
 * info@exist-db.org
6
 * http://www.exist-db.org
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
package org.exist.xquery.value;
23

24
import com.ibm.icu.text.Collator;
25
import org.apache.logging.log4j.LogManager;
26
import org.apache.logging.log4j.Logger;
27
import org.exist.xquery.*;
28
import org.exist.xquery.Constants.Comparison;
29

30
import java.util.List;
31

32
/**
33
 * Represents a function item, i.e. a reference to a function that can be called dynamically.
34
 *
35
 * @author wolf
36
 */
37
public class FunctionReference extends AtomicValue implements AutoCloseable {
38

39
    /** the expression from which this type derives */
40
    private Expression expression;
41

42
    private final static Logger LOG = LogManager.getLogger(FunctionReference.class);
4 ✔
43

44
    protected final FunctionCall functionCall;
45

46
    // Focus captured at the time of fn:function-lookup. When non-null, this
47
    // focus is substituted for the function body's evaluation, while the
48
    // function's argument expressions are still evaluated in the outer focus.
49
    // See F&O 3.1 section 16.1.1 (fn:function-lookup), which states that the
50
    // implementation of a returned context-dependent built-in is associated
51
    // with the dynamic context of the call to fn:function-lookup.
52
    private Sequence capturedContextSequence;
53
    private Item capturedContextItem;
54

55
    public FunctionReference(final FunctionCall functionCall) {
56
        this(null, functionCall);
4 ✔
57
    }
1 ✔
58

59
    public FunctionReference(final Expression expression, final FunctionCall functionCall) {
60
        super(expression);
3 ✔
61
        this.functionCall = functionCall;
3 ✔
62
    }
1 ✔
63

64
    /**
65
     * A value captured by this function's closure is still reachable through it, even though it is
66
     * not part of the sequence the defining scope returned.
67
     *
68
     * <p>Without this, a binary value captured by an inline function would be released when the scope
69
     * that created it is left, and calling the function later would find it closed.</p>
70
     *
71
     * @param item the item to look for
72
     * @return true if this reference is the item, or holds it in its closure
73
     */
74
    @Override
75
    public boolean containsReference(final Item item) {
NEW
76
        if (this == item) {
×
NEW
77
            return true;
×
78
        }
79

NEW
80
        final List<ClosureVariable> closureVariables = functionCall.getFunction().getClosureVariables();
×
NEW
81
        if (closureVariables == null) {
×
NEW
82
            return false;
×
83
        }
84

NEW
85
        for (final ClosureVariable closureVariable : closureVariables) {
×
NEW
86
            final Sequence value = closureVariable.getValue();
×
NEW
87
            if (value != null && (value == item || value.containsReference(item))) {
×
NEW
88
                return true;
×
89
            }
90
        }
91

NEW
92
        return false;
×
93
    }
94

95
    public void setCapturedContext(final Sequence contextSequence, final Item contextItem) {
96
        this.capturedContextSequence = contextSequence;
3 ✔
97
        this.capturedContextItem = contextItem;
3 ✔
98
    }
1 ✔
99

100
    private boolean hasCapturedContext() {
101
        return capturedContextSequence != null || capturedContextItem != null;
10 !
102
    }
103

104
    public FunctionCall getCall() {
105
        return functionCall;
3 ✔
106
    }
107

108
    /**
109
     * Get the signature of the function.
110
     *
111
     * @return signature of this function
112
     */
113
    public FunctionSignature getSignature() {
114
        return functionCall.getSignature();
4 ✔
115
    }
116

117
    /**
118
     * Gets the expression from which this type derives.
119
     *
120
     * @return  the expression from which this type derives
121
     */
122
    @Override
123
    public Expression getExpression() {
124
        return expression;
3 ✔
125
    }
126

127
    /**
128
     * Sets the expression from which this type derives.
129
     *
130
     * @param   expression  the expression to use
131
     */
132
    public void setExpression(final Expression expression) {
133
        this.expression = expression;
×
134
    }
×
135

136
    /**
137
     * Calls {@link FunctionCall#analyze(AnalyzeContextInfo)}.
138
     *
139
     * @param contextInfo current context information
140
     * @throws XPathException in case of static error
141
     */
142
    public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
143
        functionCall.analyze(contextInfo);
4 ✔
144
        if (functionCall.getContext().optimizationsEnabled()) {
5 !
145
            final Optimizer optimizer = new Optimizer(functionCall.getContext());
7 ✔
146
            functionCall.accept(optimizer);
4 ✔
147
            if (optimizer.hasOptimized()) {
3 ✔
148
                functionCall.resetState(true);
4 ✔
149
                functionCall.analyze(contextInfo);
4 ✔
150
            }
151
        }
152
    }
1 ✔
153

154
    /**
155
     * Evaluates the referenced function.
156
     *
157
     * @param contextSequence the input sequence
158
     * @return evaluation result of the function call
159
     * @throws XPathException in case of dynamic error
160
     */
161
    public Sequence eval(Sequence contextSequence) throws XPathException {
162
        return eval(contextSequence, null);
5 ✔
163
    }
164

165
    /**
166
     * Evaluates the referenced function.
167
     *
168
     * @param contextSequence the input sequence
169
     * @param contextItem optional: the current context item
170
     * @return evaluation result of the function call
171
     * @throws XPathException in case of dynamic error
172
     */
173
    public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
174
        if (hasCapturedContext()) {
3 ✔
175
            // Per F&O 3.1 section 16.1.1, when fn:function-lookup returns a
176
            // context-dependent built-in, the function body must see the focus
177
            // captured at lookup time. Argument expressions, however, still
178
            // evaluate against the outer focus of the dynamic call site.
179
            final int n = functionCall.getArgumentCount();
4 ✔
180
            final Sequence[] seq = new Sequence[n];
3 ✔
181
            for (int i = 0; i < n; i++) {
7 ✔
182
                seq[i] = functionCall.getArgument(i).eval(contextSequence, contextItem);
10 ✔
183
            }
184
            return functionCall.evalFunction(capturedContextSequence, capturedContextItem, seq);
9 ✔
185
        }
186
        return functionCall.eval(contextSequence, contextItem);
6 ✔
187
    }
188

189
    /**
190
     * Evaluates the referenced function.
191
     *
192
     * @param contextSequence the input sequence
193
     * @param contextItem optional: the current context item
194
     * @param seq array of parameters to be passed to the function
195
     * @return evaluation result of the function call
196
     * @throws XPathException in case of dynamic error
197
     */
198
    public Sequence evalFunction(Sequence contextSequence, Item contextItem, Sequence[] seq) throws XPathException {
199
        if (hasCapturedContext()) {
3 !
200
            return functionCall.evalFunction(capturedContextSequence, capturedContextItem, seq);
×
201
        }
202
        return functionCall.evalFunction(contextSequence, contextItem, seq);
7 ✔
203
    }
204

205
    public void setArguments(List<Expression> arguments) throws XPathException {
206
        functionCall.setArguments(arguments);
4 ✔
207
    }
1 ✔
208

209
    public void setContext(XQueryContext context) {
210
        functionCall.setContext(context);
×
211
    }
×
212

213
    @Override
214
    public void close() {
215
        resetState(false);
3 ✔
216
    }
1 ✔
217

218
    public void resetState(boolean postOptimization) {
219
//        LOG.debug("Resetting state of function item " + functionCall.getSignature().toString());
220
        functionCall.resetState(postOptimization);
4 ✔
221
    }
1 ✔
222

223
    /* (non-Javadoc)
224
     * @see org.exist.xquery.value.AtomicValue#getType()
225
     */
226
    public int getType() {
227
        return Type.FUNCTION;
2 ✔
228
    }
229

230
    /* (non-Javadoc)
231
     * @see org.exist.xquery.value.Sequence#getStringValue()
232
     */
233
    public String getStringValue() throws XPathException {
234
        return "";
×
235
    }
236

237
    /* (non-Javadoc)
238
     * @see org.exist.xquery.value.Sequence#convertTo(int)
239
     */
240
    public AtomicValue convertTo(int requiredType) throws XPathException {
241
        if (requiredType == Type.FUNCTION) {
×
242
            return this;
×
243
        }
244
        throw new XPathException(getExpression(), ErrorCodes.FORG0001, "cannot convert function reference to " + Type.getTypeName(requiredType));
×
245
    }
246

247
    public boolean effectiveBooleanValue() throws XPathException {
248
        throw new XPathException(getExpression(), ErrorCodes.FORG0006, "Effective boolean value is not defined for a FunctionReference");
8 ✔
249
    }
250

251
    @Override
252
    public boolean compareTo(Collator collator, Comparison operator, AtomicValue other)
253
            throws XPathException {
254
        throw new XPathException(getExpression(), "cannot compare function reference to " + Type.getTypeName(other.getType()));
×
255
    }
256

257
    public int compareTo(Collator collator, AtomicValue other)
258
            throws XPathException {
259
        throw new XPathException(getExpression(), "cannot compare function reference to " + Type.getTypeName(other.getType()));
×
260
    }
261

262
    public AtomicValue max(Collator collator, AtomicValue other)
263
            throws XPathException {
264
        throw new XPathException(getExpression(), "Invalid argument to aggregate function: cannot compare function references");
×
265
    }
266

267
    public AtomicValue min(Collator collator, AtomicValue other)
268
            throws XPathException {
269
        throw new XPathException(getExpression(), "Invalid argument to aggregate function: cannot compare function references");
×
270
    }
271

272
    @Override
273
    public AtomicValue atomize() throws XPathException {
274
        throw new XPathException(getExpression(), ErrorCodes.FOTY0013, "A function item other than an array cannot be atomized");
8 ✔
275
    }
276

277
    @Override
278
    public String toString() {
279
        return "anonymous-function#" + functionCall.getArgumentCount();
×
280
    }
281
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc