summaryrefslogtreecommitdiff
path: root/src/xmlpatterns/expr/quserfunctioncallsite.cpp
blob: c3ce2cd66efed5548570cb33562d3dd18abaa2a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtXmlPatterns module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qcommonsequencetypes_p.h"
#include "qdynamiccontextstore_p.h"
#include "qevaluationcache_p.h"

#include "quserfunctioncallsite_p.h"

QT_BEGIN_NAMESPACE

using namespace QPatternist;

UserFunctionCallsite::UserFunctionCallsite(const QXmlName nameP,
                                           const FunctionSignature::Arity ar) : CallSite(nameP)
                                                                              , m_arity(ar)
                                                                              , m_expressionSlotOffset(-2)

{
}

Item::Iterator::Ptr UserFunctionCallsite::evaluateSequence(const DynamicContext::Ptr &context) const
{
    return m_body->evaluateSequence(bindVariables(context));
}

Item UserFunctionCallsite::evaluateSingleton(const DynamicContext::Ptr &context) const
{
    return m_body->evaluateSingleton(bindVariables(context));
}

bool UserFunctionCallsite::evaluateEBV(const DynamicContext::Ptr &context) const
{
    return m_body->evaluateEBV(bindVariables(context));
}

void UserFunctionCallsite::evaluateToSequenceReceiver(const DynamicContext::Ptr &context) const
{
    m_body->evaluateToSequenceReceiver(bindVariables(context));
}

DynamicContext::Ptr UserFunctionCallsite::bindVariables(const DynamicContext::Ptr &context) const
{
    const DynamicContext::Ptr stackContext(context->createStack());
    Q_ASSERT(stackContext);

    const Expression::List::const_iterator end(m_operands.constEnd());
    Expression::List::const_iterator it(m_operands.constBegin());

    VariableSlotID slot = m_expressionSlotOffset;

    for(; it != end; ++it)
    {
        stackContext->setExpressionVariable(slot,
                                            Expression::Ptr(new DynamicContextStore(*it, context)));
        ++slot;
    }

    return stackContext;
}

SequenceType::List UserFunctionCallsite::expectedOperandTypes() const
{
    SequenceType::List result;

    if(m_functionDeclaration)
    {
        const FunctionArgument::List args(m_functionDeclaration->signature()->arguments());
        const FunctionArgument::List::const_iterator end(args.constEnd());
        FunctionArgument::List::const_iterator it(args.constBegin());

        for(; it != end; ++it)
            result.append((*it)->type());
    }
    else
        result.append(CommonSequenceTypes::ZeroOrMoreItems);

    return result;
}

Expression::Ptr UserFunctionCallsite::typeCheck(const StaticContext::Ptr &context,
                                                const SequenceType::Ptr &reqType)
{
    /* The parser calls TypeChecker::applyFunctionConversion() on user function
     * bodies, possibly indirectly, before all function call sites have been
     * resolved. Hence it's possible that we're called before before the usual
     * typeCheck() pass, and hence before we have been resolved/checked and
     * subsequently m_functionDeclaration set. Therefore, encounter for that below.
     *
     * UnresolvedVariableReference::typeCheck() has the same dilemma.
     */

    /* Ensure that the return value of the function is properly
     * converted/does match from where it is called(which is here). */
    if(isRecursive() || !m_functionDeclaration)
        return CallSite::typeCheck(context, reqType);
    else
    {
        /* Update, such that we use a recent version of the body that has typeCheck()
         * and compress() rewrites included. */
        m_body = m_functionDeclaration->body();

        /* Note, we can't assign to m_functionDeclaration->body() because UserFunction can apply
         * to several different callsites. Hence we need our own version. */
        m_body = m_body->typeCheck(context, reqType);

        /* We just act as a pipe for m_body, so we don't have to typecheck ourselves. However,
         * the arguments must match the function declaration. */
        typeCheckOperands(context);
        return Expression::Ptr(this);
    }
}

Expression::Ptr UserFunctionCallsite::compress(const StaticContext::Ptr &context)
{
    if(!isRecursive())
        rewrite(m_body, m_body->compress(context), context);

    return CallSite::compress(context);
}

Expression::Properties UserFunctionCallsite::properties() const
{
    return DisableElimination;
}

SequenceType::Ptr UserFunctionCallsite::staticType() const
{
    /* Our return type, is the static type of the function body. We could have also used
     * m_functionDeclaration->signature()->returnType(), but it doesn't get updated
     * when function conversion is applied.
     * We can't use m_body's type if we're recursive, because m_body computes its type
     * from its children, and we're at least one of the children. Hence, we would
     * recurse infinitely if we did.
     *
     * m_body can be null here if we're called before setSource().
     */
    if(isRecursive() || !m_body)
        return CommonSequenceTypes::ZeroOrMoreItems; // TODO use the declaration, it can have a type explicitly.
    else
        return m_body->staticType();
}

ExpressionVisitorResult::Ptr UserFunctionCallsite::accept(const ExpressionVisitor::Ptr &visitor) const
{
    return visitor->visit(this);
}

Expression::ID UserFunctionCallsite::id() const
{
    return IDUserFunctionCallsite;
}

bool UserFunctionCallsite::isSignatureValid(const FunctionSignature::Ptr &sign) const
{
    Q_ASSERT(sign);

    return sign->name() == name()
           &&
           sign->isArityValid(m_arity);
}

bool UserFunctionCallsite::configureRecursion(const CallTargetDescription::Ptr &sign)
{
    Q_ASSERT(sign);

    setIsRecursive(isSignatureValid(sign));
    return isRecursive();
}

void UserFunctionCallsite::setSource(const UserFunction::Ptr &userFunction,
                                     const VariableSlotID cacheSlotOffset)
{
    m_functionDeclaration = userFunction;
    m_body = userFunction->body();
    m_expressionSlotOffset = userFunction->expressionSlotOffset();

    const int len = m_operands.size();

    const VariableDeclaration::List varDecls(userFunction->argumentDeclarations());

    for(int i = 0; i < len; ++i)
    {
        /* We don't want evaluation caches for range variables, it's not necessary since
         * the item is already cached in DynamicContext::rangeVariable(). */
        if(m_operands.at(i)->is(IDRangeVariableReference))
            continue;

        /* Note that we pass in cacheSlotOffset + i here instead of varDecls.at(i)->slot since
         * we want independent caches for each callsite. */
        m_operands[i] = Expression::Ptr(new EvaluationCache<false>(m_operands.at(i),
                                                                   varDecls.at(i).data(),
                                                                   cacheSlotOffset + i));
    }
}

FunctionSignature::Arity UserFunctionCallsite::arity() const
{
    return m_arity;
}

CallTargetDescription::Ptr UserFunctionCallsite::callTargetDescription() const
{
    return m_functionDeclaration->signature();
}

QT_END_NAMESPACE