Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljsfunctioninitializer.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include <private/qqmljsmemorypool_p.h>
7
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/qfileinfo.h>
10
12
13using namespace Qt::StringLiterals;
14
25{
26 switch (type) {
28 return u"invalid"_s;
30 return u"a boolean"_s;
32 return u"a number"_s;
34 return u"a string"_s;
36 return u"null"_s;
38 return u"a translation"_s;
40 return u"a translation by id"_s;
42 return u"a script"_s;
44 return u"an object"_s;
46 return u"an attached property"_s;
48 return u"a grouped property"_s;
49 }
50
51 return u"nothing"_s;
52}
53
54void QQmlJSFunctionInitializer::populateSignature(
57{
58 const auto signatureError = [&](const QString &message) {
59 error->type = QtWarningMsg;
60 error->loc = ast->firstSourceLocation();
61 error->message = message;
62 function->isFullyTyped = false;
63 };
64
65 if (!m_typeResolver->canCallJSFunctions()) {
66 signatureError(u"Ignoring type annotations as requested "
67 "by pragma FunctionSignatureBehavior"_s);
68 return;
69 }
70
72 if (ast->formals)
73 arguments = ast->formals->formals();
74
75 // If the function has no arguments and no return type annotation we assume it's untyped.
76 // You can annotate it to return void to make it typed.
77 // Otherwise we first assume it's typed and reset the flag if we detect a problem.
78 function->isFullyTyped = !arguments.isEmpty() || ast->typeAnnotation;
79
80 if (function->argumentTypes.isEmpty()) {
81 for (const QQmlJS::AST::BoundName &argument : std::as_const(arguments)) {
82 if (argument.typeAnnotation) {
83 if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) {
84 function->argumentTypes.append(
85 m_typeResolver->tracked(
86 m_typeResolver->globalType(type)));
87 } else {
88 function->argumentTypes.append(
89 m_typeResolver->tracked(
90 m_typeResolver->globalType(m_typeResolver->varType())));
91 signatureError(u"Cannot resolve the argument type %1."_s
92 .arg(argument.typeAnnotation->type->toString()));
93 }
94 } else {
95 function->argumentTypes.append(
96 m_typeResolver->tracked(
97 m_typeResolver->globalType(m_typeResolver->varType())));
98 signatureError(u"Functions without type annotations won't be compiled"_s);
99 }
100 }
101 } else {
102 for (qsizetype i = 0, end = arguments.size(); i != end; ++i) {
104 if (argument.typeAnnotation) {
105 if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) {
106 if (!m_typeResolver->registerContains(function->argumentTypes[i], type)) {
107 signatureError(u"Type annotation %1 on signal handler "
108 "contradicts signal argument type %2"_s
109 .arg(argument.typeAnnotation->type->toString(),
110 function->argumentTypes[i].descriptiveName()));
111 }
112 }
113 }
114 }
115 }
116
117 if (!function->returnType) {
118 if (ast->typeAnnotation) {
119 function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
120 if (!function->returnType)
121 signatureError(u"Cannot resolve return type %1"_s.arg(
123 }
124 }
125
126 for (int i = QQmlJSCompilePass::FirstArgument + function->argumentTypes.size();
127 i < context->registerCountInFunction; ++i) {
128 function->registerTypes.append(m_typeResolver->tracked(
129 m_typeResolver->globalType(m_typeResolver->voidType())));
130 }
131
132 function->addressableScopes = m_typeResolver->objectsById();
133 function->code = context->code;
134 function->sourceLocations = context->sourceLocationTable.get();
135}
136
137static void diagnose(
140{
142 message,
143 type,
145 };
146}
147
150 const QString &propertyName,
151 QQmlJS::AST::Node *astNode,
152 const QmlIR::Binding &irBinding,
154{
155 QQmlJS::SourceLocation bindingLocation;
156 bindingLocation.startLine = irBinding.location.line();
157 bindingLocation.startColumn = irBinding.location.column();
158
160 function.qmlScope = m_scopeType;
161
162 if (irBinding.type() != QmlIR::Binding::Type_Script) {
163 diagnose(u"Binding is not a script binding, but %1."_s.arg(
165 QtDebugMsg, bindingLocation, error);
166 }
167
168 function.isProperty = m_objectType->hasProperty(propertyName);
169 if (!function.isProperty && QmlIR::IRBuilder::isSignalPropertyName(propertyName)) {
170 const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName(propertyName);
171
172 if (signalName.endsWith(u"Changed"_s)
173 && m_objectType->hasProperty(signalName.chopped(strlen("Changed")))) {
174 function.isSignalHandler = true;
175 } else {
176 const auto methods = m_objectType->methods(signalName);
177 for (const auto &method : methods) {
178 if (method.isCloned())
179 continue;
180 if (method.methodType() == QQmlJSMetaMethodType::Signal) {
181 function.isSignalHandler = true;
182 const auto arguments = method.parameters();
183 for (qsizetype i = 0, end = arguments.size(); i < end; ++i) {
184 const auto &type = arguments[i].type();
185 if (type.isNull()) {
186 diagnose(u"Cannot resolve the argument type %1."_s.arg(
187 arguments[i].typeName()),
188 QtDebugMsg, bindingLocation, error);
189 function.argumentTypes.append(
190 m_typeResolver->tracked(
191 m_typeResolver->globalType(m_typeResolver->varType())));
192 } else {
193 function.argumentTypes.append(m_typeResolver->tracked(
194 m_typeResolver->globalType(type)));
195 }
196 }
197 break;
198 }
199 }
200 }
201
202 if (!function.isSignalHandler) {
203 diagnose(u"Could not compile signal handler for %1: The signal does not exist"_s.arg(
204 signalName),
205 QtWarningMsg, bindingLocation, error);
206 }
207 }
208
209 if (!function.isSignalHandler) {
210 if (!function.isProperty) {
211 diagnose(u"Could not compile binding for %1: The property does not exist"_s.arg(
212 propertyName),
213 QtWarningMsg, bindingLocation, error);
214 }
215
216 const auto property = m_objectType->property(propertyName);
217 if (const QQmlJSScope::ConstPtr propertyType = property.type()) {
218 function.returnType = propertyType->isListProperty()
219 ? m_typeResolver->qObjectListType()
220 : propertyType;
221 } else {
222 QString message = u"Cannot resolve property type %1 for binding on %2."_s
223 .arg(property.typeName(), propertyName);
224 if (m_objectType->isNameDeferred(propertyName)) {
225 // If the property doesn't exist but the name is deferred, then
226 // it's deferred via the presence of immediate names. Those are
227 // most commonly used to enable generalized grouped properties.
228 message += u" You may want use ID-based grouped properties here.";
229 }
230 diagnose(message, QtWarningMsg, bindingLocation, error);
231 }
232
233 if (!property.bindable().isEmpty() && !property.isPrivate())
234 function.isQPropertyBinding = true;
235 }
236
238 auto ast = astNode->asFunctionDefinition();
239 if (!ast) {
240 QQmlJS::AST::Statement *stmt = astNode->statementCast();
241 if (!stmt) {
242 Q_ASSERT(astNode->expressionCast());
244 stmt = new (&pool) QQmlJS::AST::ExpressionStatement(expr);
245 }
246 auto body = new (&pool) QQmlJS::AST::StatementList(stmt);
247 body = body->finish();
248
249 QString name = u"binding for "_s; // ####
251 pool.newString(name), /*formals*/ nullptr, body);
252 ast->lbraceToken = astNode->firstSourceLocation();
253 ast->functionToken = ast->lbraceToken;
254 ast->rbraceToken = astNode->lastSourceLocation();
255 }
256
257 populateSignature(context, ast, &function, error);
258 return function;
259}
260
263 const QString &functionName, QQmlJS::AST::Node *astNode,
265{
266 Q_UNUSED(functionName);
267
269 function.qmlScope = m_scopeType;
270
271 auto ast = astNode->asFunctionDefinition();
272 Q_ASSERT(ast);
273
274 populateSignature(context, ast, &function, error);
275 return function;
276}
277
static JNINativeMethod methods[]
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
QQmlJSCompilePass::Function run(const QV4::Compiler::Context *context, const QString &propertyName, QQmlJS::AST::Node *astNode, const QmlIR::Binding &irBinding, QQmlJS::DiagnosticMessage *error)
bool isNameDeferred(const QString &name) const
QHash< QString, QQmlJSMetaMethod > methods() const
Returns all methods visible from this scope including those of base types and extensions.
QQmlJSMetaProperty property(const QString &name) const
bool hasProperty(const QString &name) const
const QQmlJSScopesById & objectsById() const
QQmlJSRegisterContent tracked(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr qObjectListType() const
bool registerContains(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr voidType() const
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const
QQmlJSScope::ConstPtr varType() const
FormalParameterList * formals
SourceLocation firstSourceLocation() const override
virtual SourceLocation firstSourceLocation() const =0
virtual Statement * statementCast()
Definition qqmljsast.cpp:49
virtual SourceLocation lastSourceLocation() const =0
virtual ExpressionNode * expressionCast()
Definition qqmljsast.cpp:39
virtual FunctionExpression * asFunctionDefinition()
Definition qqmljsast.cpp:69
StatementList * finish()
UiQualifiedId * typeId
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
QString chopped(qsizetype n) const
Definition qstring.h:345
QList< QVariant > arguments
Combined button and popup list for selecting options.
static void * context
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
QtMsgType
Definition qlogging.h:29
@ QtWarningMsg
Definition qlogging.h:31
@ QtDebugMsg
Definition qlogging.h:30
GLint location
GLuint GLuint end
GLenum type
GLuint GLsizei const GLchar * message
GLuint name
static void diagnose(const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location, QQmlJS::DiagnosticMessage *error)
static QString bindingTypeDescription(QmlIR::Binding::Type type)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
ptrdiff_t qsizetype
Definition qtypes.h:70
const char property[13]
Definition qwizard.cpp:101
QDBusArgument argument
static QString signalNameFromSignalPropertyName(const QString &signalPropertyName)
static bool isSignalPropertyName(const QString &name)
static QString asString(QQmlJS::AST::UiQualifiedId *node)