Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljslookupsignatures.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant
4
6
7#include <private/qqmljstyperesolver_p.h>
8
9using namespace Qt::StringLiterals;
10
11QT_BEGIN_NAMESPACE
12
13QQmlJSLookupSignaturesRecorder::QQmlJSLookupSignaturesRecorder(
14 const QString &currentFilePath, const QQmlJSTypeResolver *typeResolver)
15 : m_currentFilePath(currentFilePath), m_typeResolver(typeResolver)
16{
17 Q_ASSERT(!m_currentFilePath.isEmpty());
18 Q_ASSERT(m_typeResolver);
19}
20
21QQmlPrivate::AOTLookupValidation::Type QQmlJSLookupSignaturesRecorder::type(
22 const QQmlJSScope::ConstPtr &type)
23{
24 using namespace QQmlPrivate::AOTLookupValidation;
25
26 QQmlPrivate::AOTLookupValidation::Type res;
27 if (!type->isComposite()) {
28 if (type->accessSemantics() == QQmlSA::AccessSemantics::Value && type->isSelfExtension()) {
29 res.name = type->internalName();
30 res.icNameOrExtensionTypeName = type->extensionTypeName();
31 } else {
32 res.name = type->internalName();
33 }
34
35 return res;
36 }
37
38 res.isComposite = IsComposite::Yes;
39 if (isUnnamedCompositeType(type)) {
40 // An unnamed type can only be used as the base of a lookup, not the type in a property or
41 // method declaration. Assume the target property/method is declared on a base type, and
42 // record the base type of \a type as the base of the lookup. Otherwise, we would have
43 // discarded the lookup earlier. See recordPropertyLookup.
44 Q_ASSERT(type->baseType());
45 return QQmlJSLookupSignaturesRecorder::type(type->baseType());
46 }
47
48 QQmlJSScope::ConstPtr root = type;
49 while (!root->isFileRootComponent())
50 root = root->parentScope();
51
52 bool isFileBeingCompiled = root->filePath() == m_typeResolver->logger()->filePath();
53
54 Q_ASSERT(!root->internalName().isEmpty());
55 res.module = isFileBeingCompiled ? s_thisCuModule : root->moduleName();
56 res.name = isFileBeingCompiled ? s_thisCuType : root->internalName();
57
58 if (type->isInlineComponent()) {
59 res.isInlineComponent = IsIC::Yes;
60 res.icNameOrExtensionTypeName = *type->inlineComponentName();
61 return res;
62 }
63
64 return res;
65}
66
67bool QQmlJSLookupSignaturesRecorder::cantDesync(const QQmlJSScope::ConstPtr &type) const
68{
69 Q_ASSERT(!m_typeResolver->logger()->filePath().isEmpty());
70 // Same file -> If one changes the other also gets recompiled and adapts to the change
71 return type->filePath() == m_typeResolver->logger()->filePath();
72}
73
74bool QQmlJSLookupSignaturesRecorder::safeBase(const QQmlJSScope::ConstPtr &base) const
75{
76 return base == m_typeResolver->varType() || base == m_typeResolver->jsValueType()
77 || base->inherits(m_typeResolver->qmlPropertyMapType());
78}
79
80bool QQmlJSLookupSignaturesRecorder::isUnnamedCompositeType(const QQmlJSScope::ConstPtr &type) const
81{
82 return type->isComposite() && !type->isFileRootComponent() && !type->isInlineComponent();
83}
84
85static QQmlJSMetaMethod::RelativeFunctionIndex methodIndex(const QQmlJSMetaMethod &m)
86{
87 return m.otherMethodIndex() == QQmlJSMetaMethod::RelativeFunctionIndex::Invalid
88 ? m.methodIndex() : m.otherMethodIndex();
89}
90
91// On the MetaObject, signals come before regular functions
92static int ownRegularMethodCountBeforeIndex(const QQmlJSScope::ConstPtr &type, int index)
93{
94 const auto &ms = type->ownMethods();
95 return std::count_if(ms.cbegin(), ms.cend(), [&](const QQmlJSMetaMethod &m) {
96 Q_ASSERT(m.isConstructor() || methodIndex(m) != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
97 return m.methodType() != QQmlSA::MethodType::Signal && !m.isConstructor()
98 && int(methodIndex(m)) < index;
99 });
100}
101
102// On the MetaObject, signals come before regular functions
103static int ownSignalCountAfterIndex(const QQmlJSScope::ConstPtr &type, int index)
104{
105 const auto &ms = type->ownMethods();
106 return std::count_if(ms.cbegin(), ms.cend(), [&](const QQmlJSMetaMethod &m) {
107 Q_ASSERT(m.isConstructor() || methodIndex(m) != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
108 return m.methodType() == QQmlSA::MethodType::Signal && int(methodIndex(m)) > index;
109 });
110}
111
112// On the MetaObject, regular properties come before aliases
113static int ownAliasCountBeforeIndex(const QQmlJSScope::ConstPtr &type, int index)
114{
115 const auto &ps = type->ownProperties();
116 return std::count_if(ps.cbegin(), ps.cend(), [&](const auto &p) {
117 return p.isAlias() && p.index() < index;
118 });
119}
120
121// On the MetaObject, regular properties come before aliases
122static int ownRegularPropertyCountAfterIndex(const QQmlJSScope::ConstPtr &type, int index)
123{
124 const auto &ps = type->ownProperties();
125 return std::count_if(ps.cbegin(), ps.cend(), [&](const auto &p) {
126 return !p.isAlias() && p.index() > index;
127 });
128}
129
130void QQmlJSLookupSignaturesRecorder::recordPropertyLookup(const QQmlJSScope::ConstPtr &base,
131 const QQmlJSMetaProperty &property)
132{
133 using namespace QQmlPrivate::AOTLookupValidation;
134
135 const QString &name = property.propertyName();
136 const auto [owner, extensionSpecifier] = QQmlJSScope::ownerOfProperty(base, name);
137 if (base->isScript() || safeBase(base) || cantDesync(owner))
138 return;
139
140 // When performing a lookup on an inner object of unnamed type, one of two things is true:
141 // 1) The target property (or method) is declared within the object. Then, because the object
142 // is unnamed, it must be defined in the same file as the lookup and they cannot desync and
143 // we don't have to record it.
144 // 2) Or the target property is declared on a base type of the inner object. Then we can
145 // simply record the lookup as a being performed on the base type instead. The addition of
146 // a property on the inner object that would shadow the base type's property can only be
147 // introduced by recompiling the lookup as well.
148 if (isUnnamedCompositeType(base) && owner == base)
149 return;
150
151 PropertySignature propertySignature;
152 propertySignature.type = type(property.type());
153
154 // Compiler indexes follow document order. On the MO, aliases come after regular properties.
155 propertySignature.relativeIndex = property.isAlias()
156 ? property.index() + ownRegularPropertyCountAfterIndex(owner, property.index())
157 : property.index() - ownAliasCountBeforeIndex(owner, property.index());
158
159 Lookup lookup;
160 lookup.base = type(base);
161 lookup.member = name;
162
163 m_signatures.insert(lookup, propertySignature);
164}
165
166void QQmlJSLookupSignaturesRecorder::recordMethodLookup(const QQmlJSScope::ConstPtr &base,
167 const QQmlJSMetaMethod &method)
168{
169 using namespace QQmlPrivate::AOTLookupValidation;
170
171 const QString &name = method.methodName();
172 const auto [owner, extensionSpecifier] = QQmlJSScope::ownerOfMethod(base, name);
173 if (base->isScript() || safeBase(base) || cantDesync(owner))
174 return;
175
176 // See recordPropertyLookup
177 if (isUnnamedCompositeType(base) && owner == base)
178 return;
179
180 // destroy and toString are special?
181 if (name == QStringLiteral("destroy") || name == QStringLiteral("toString"))
182 return;
183
184 MethodSignature methodSignature;
185
186 if (method.methodType() == QQmlSA::MethodType::Signal) {
187 methodSignature.isSignal = IsSignal::Yes;
188 int index = int(methodIndex(method));
189 methodSignature.relativeIndex = index - ownRegularMethodCountBeforeIndex(owner, index);
190 } else {
191 methodSignature.isSignal = IsSignal::No;
192 int index = int(methodIndex(method));
193 methodSignature.relativeIndex = index + ownSignalCountAfterIndex(owner, index);
194 }
195
196 methodSignature.types.push_back(type(method.returnType()));
197 for (const auto &param : method.parameters()) {
198 methodSignature.paramNames.push_back(param.name());
199 methodSignature.types.push_back(type(param.type()));
200 }
201
202 Lookup lookup;
203 lookup.base = type(base);
204 lookup.member = name;
205
206 m_signatures.insert(lookup, methodSignature);
207}
208
209void QQmlJSLookupSignaturesRecorder::recordEnumKeyLookup(const QQmlJSScope::ConstPtr &base,
210 const QQmlJSMetaEnum &metaEnum,
211 const QString &keyName)
212{
213 using namespace QQmlPrivate::AOTLookupValidation;
214
215 const auto [owner, extensionSpecifier] = QQmlJSScope::ownerOfEnum(base, metaEnum.name());
216 if (base->isScript() || safeBase(base) || cantDesync(owner))
217 return;
218
219 EnumKeySignature enumSignature;
220 // QTBUG-145053: enums can only hold ints in the Compiler
221 enumSignature.value = quint64(metaEnum.value(keyName));
222 if (metaEnum.isFlag())
223 enumSignature.isFlag = IsFlag::Yes;
224
225 Lookup lookup;
226 lookup.base = type(base);
227 lookup.member = keyName;
228 lookup.enumName = metaEnum.name();
229 m_signatures.insert(lookup, enumSignature);
230}
231
232QT_END_NAMESPACE
static int ownAliasCountBeforeIndex(const QQmlJSScope::ConstPtr &type, int index)
static int ownRegularMethodCountBeforeIndex(const QQmlJSScope::ConstPtr &type, int index)
static int ownRegularPropertyCountAfterIndex(const QQmlJSScope::ConstPtr &type, int index)
static QQmlJSMetaMethod::RelativeFunctionIndex methodIndex(const QQmlJSMetaMethod &m)
static int ownSignalCountAfterIndex(const QQmlJSScope::ConstPtr &type, int index)