1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmlmetatypedata_p.h" |
5 | |
6 | #include <private/qqmltype_p_p.h> |
7 | #include <private/qqmltypemodule_p.h> |
8 | #include <private/qqmlpropertycache_p.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | QQmlMetaTypeData::QQmlMetaTypeData() |
13 | { |
14 | } |
15 | |
16 | QQmlMetaTypeData::~QQmlMetaTypeData() |
17 | { |
18 | for (auto iter = compositeTypes.cbegin(), end = compositeTypes.cend(); iter != end; ++iter) |
19 | iter.value()->isRegistered = false; |
20 | |
21 | propertyCaches.clear(); |
22 | // Do this before the attached properties disappear. |
23 | types.clear(); |
24 | undeletableTypes.clear(); |
25 | qDeleteAll(c: metaTypeToValueType); |
26 | } |
27 | |
28 | // This expects a "fresh" QQmlTypePrivate and adopts its reference. |
29 | void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) |
30 | { |
31 | for (int i = 0; i < types.size(); ++i) { |
32 | if (!types.at(i).isValid()) { |
33 | types[i] = QQmlType(priv); |
34 | priv->index = i; |
35 | priv->release(); |
36 | return; |
37 | } |
38 | } |
39 | types.append(t: QQmlType(priv)); |
40 | priv->index = types.size() - 1; |
41 | priv->release(); |
42 | } |
43 | |
44 | QQmlMetaTypeData::VersionedUri::VersionedUri(const std::unique_ptr<QQmlTypeModule> &module) |
45 | : uri(module->module()), majorVersion(module->majorVersion()) |
46 | { |
47 | } |
48 | |
49 | QQmlTypeModule *QQmlMetaTypeData::findTypeModule(const QString &module, QTypeRevision version) |
50 | { |
51 | const auto qqtm = std::lower_bound( |
52 | first: uriToModule.begin(), last: uriToModule.end(), val: VersionedUri(module, version), |
53 | comp: std::less<QQmlMetaTypeData::VersionedUri>()); |
54 | if (qqtm == uriToModule.end()) |
55 | return nullptr; |
56 | |
57 | QQmlTypeModule *candidate = qqtm->get(); |
58 | return (candidate->module() == module && candidate->majorVersion() == version.majorVersion()) |
59 | ? candidate |
60 | : nullptr; |
61 | } |
62 | |
63 | QQmlTypeModule *QQmlMetaTypeData::addTypeModule(std::unique_ptr<QQmlTypeModule> module) |
64 | { |
65 | QQmlTypeModule *ret = module.get(); |
66 | uriToModule.emplace_back(args: std::move(module)); |
67 | std::sort(first: uriToModule.begin(), last: uriToModule.end(), |
68 | comp: [](const std::unique_ptr<QQmlTypeModule> &a, |
69 | const std::unique_ptr<QQmlTypeModule> &b) { |
70 | const int diff = a->module().compare(s: b->module()); |
71 | return diff < 0 || (diff == 0 && a->majorVersion() < b->majorVersion()); |
72 | }); |
73 | return ret; |
74 | } |
75 | |
76 | bool QQmlMetaTypeData::registerModuleTypes(const QString &uri) |
77 | { |
78 | auto function = moduleTypeRegistrationFunctions.constFind(key: uri); |
79 | if (function != moduleTypeRegistrationFunctions.constEnd()) { |
80 | (*function)(); |
81 | return true; |
82 | } |
83 | return false; |
84 | } |
85 | |
86 | QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCacheForVersion( |
87 | int index, QTypeRevision version) const |
88 | { |
89 | return (index < typePropertyCaches.size()) |
90 | ? typePropertyCaches.at(i: index).value(key: version) |
91 | : QQmlPropertyCache::ConstPtr(); |
92 | } |
93 | |
94 | void QQmlMetaTypeData::setPropertyCacheForVersion(int index, QTypeRevision version, |
95 | const QQmlPropertyCache::ConstPtr &cache) |
96 | { |
97 | if (index >= typePropertyCaches.size()) |
98 | typePropertyCaches.resize(size: index + 1); |
99 | typePropertyCaches[index][version] = cache; |
100 | } |
101 | |
102 | void QQmlMetaTypeData::clearPropertyCachesForVersion(int index) |
103 | { |
104 | if (index < typePropertyCaches.size()) |
105 | typePropertyCaches[index].clear(); |
106 | } |
107 | |
108 | QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache( |
109 | const QMetaObject *metaObject, QTypeRevision version) |
110 | { |
111 | if (QQmlPropertyCache::ConstPtr rv = propertyCaches.value(key: metaObject)) |
112 | return rv; |
113 | |
114 | QQmlPropertyCache::ConstPtr rv; |
115 | if (const QMetaObject *superMeta = metaObject->superClass()) |
116 | rv = propertyCache(metaObject: superMeta, version)->copyAndAppend(metaObject, typeVersion: version); |
117 | else |
118 | rv = QQmlPropertyCache::createStandalone(metaObject); |
119 | |
120 | const auto *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data); |
121 | if (!(mop->flags & DynamicMetaObject)) |
122 | propertyCaches.insert(key: metaObject, value: rv); |
123 | |
124 | return rv; |
125 | } |
126 | |
127 | QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache( |
128 | const QQmlType &type, QTypeRevision version) |
129 | { |
130 | Q_ASSERT(type.isValid()); |
131 | |
132 | if (auto pc = propertyCacheForVersion(index: type.index(), version)) |
133 | return pc; |
134 | |
135 | QVector<QQmlType> types; |
136 | |
137 | quint8 maxMinorVersion = 0; |
138 | |
139 | const QMetaObject *metaObject = type.metaObject(); |
140 | Q_ASSERT(metaObject); |
141 | |
142 | const QTypeRevision combinedVersion = version.hasMajorVersion() |
143 | ? version |
144 | : (version.hasMinorVersion() |
145 | ? QTypeRevision::fromVersion(majorVersion: type.version().majorVersion(), |
146 | minorVersion: version.minorVersion()) |
147 | : QTypeRevision::fromMajorVersion(majorVersion: type.version().majorVersion())); |
148 | |
149 | while (metaObject) { |
150 | QQmlType t = QQmlMetaType::qmlType(metaObject, module: type.module(), version: combinedVersion); |
151 | if (t.isValid()) { |
152 | maxMinorVersion = qMax(a: maxMinorVersion, b: t.version().minorVersion()); |
153 | types << t; |
154 | } else { |
155 | types << QQmlType(); |
156 | } |
157 | |
158 | metaObject = metaObject->superClass(); |
159 | } |
160 | |
161 | const QTypeRevision maxVersion = QTypeRevision::fromVersion(majorVersion: combinedVersion.majorVersion(), |
162 | minorVersion: maxMinorVersion); |
163 | if (auto pc = propertyCacheForVersion(index: type.index(), version: maxVersion)) { |
164 | setPropertyCacheForVersion(index: type.index(), version: maxVersion, cache: pc); |
165 | return pc; |
166 | } |
167 | |
168 | QQmlPropertyCache::ConstPtr raw = propertyCache(metaObject: type.metaObject(), version: combinedVersion); |
169 | QQmlPropertyCache::Ptr copied; |
170 | |
171 | for (int ii = 0; ii < types.size(); ++ii) { |
172 | const QQmlType ¤tType = types.at(i: ii); |
173 | if (!currentType.isValid()) |
174 | continue; |
175 | |
176 | QTypeRevision rev = currentType.metaObjectRevision(); |
177 | int moIndex = types.size() - 1 - ii; |
178 | |
179 | if (raw->allowedRevision(index: moIndex) != rev) { |
180 | if (copied.isNull()) { |
181 | copied = raw->copy(); |
182 | raw = copied; |
183 | } |
184 | copied->setAllowedRevision(index: moIndex, allowed: rev); |
185 | } |
186 | } |
187 | |
188 | // Test revision compatibility - the basic rule is: |
189 | // * Anything that is excluded, cannot overload something that is not excluded * |
190 | |
191 | // Signals override: |
192 | // * other signals and methods of the same name. |
193 | // * properties named on<Signal Name> |
194 | // * automatic <property name>Changed notify signals |
195 | |
196 | // Methods override: |
197 | // * other methods of the same name |
198 | |
199 | // Properties override: |
200 | // * other elements of the same name |
201 | |
202 | #if 0 |
203 | bool overloadError = false; |
204 | QString overloadName; |
205 | |
206 | for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); |
207 | !overloadError && iter != raw->stringCache.end(); |
208 | ++iter) { |
209 | |
210 | const QQmlPropertyData *d = *iter; |
211 | if (raw->isAllowedInRevision(d)) |
212 | continue; // Not excluded - no problems |
213 | |
214 | // check that a regular "name" overload isn't happening |
215 | const QQmlPropertyData *current = d; |
216 | while (!overloadError && current) { |
217 | current = d->overrideData(current); |
218 | if (current && raw->isAllowedInRevision(current)) |
219 | overloadError = true; |
220 | } |
221 | } |
222 | |
223 | if (overloadError) { |
224 | if (hasCopied) raw->release(); |
225 | |
226 | error.setDescription(QLatin1String("Type " ) + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"" ) + overloadName + QLatin1String("\". This is an error in the type's implementation." )); |
227 | return 0; |
228 | } |
229 | #endif |
230 | |
231 | setPropertyCacheForVersion(index: type.index(), version, cache: raw); |
232 | |
233 | if (version != maxVersion) |
234 | setPropertyCacheForVersion(index: type.index(), version: maxVersion, cache: raw); |
235 | |
236 | return raw; |
237 | } |
238 | |
239 | static QQmlPropertyCache::ConstPtr propertyCacheForPotentialInlineComponentType( |
240 | QMetaType t, |
241 | const QHash<const QtPrivate::QMetaTypeInterface *, |
242 | QV4::ExecutableCompilationUnit *>::const_iterator &iter) { |
243 | if (t != (*iter)->typeIds.id) { |
244 | // this is an inline component, and what we have in the iterator is currently the parent compilation unit |
245 | for (auto &&icDatum: (*iter)->inlineComponentData) |
246 | if (icDatum.typeIds.id == t) |
247 | return (*iter)->propertyCaches.at(index: icDatum.objectIndex); |
248 | } |
249 | return (*iter)->rootPropertyCache(); |
250 | } |
251 | |
252 | QQmlPropertyCache::ConstPtr QQmlMetaTypeData::findPropertyCacheInCompositeTypes(QMetaType t) const |
253 | { |
254 | auto iter = compositeTypes.constFind(key: t.iface()); |
255 | return (iter == compositeTypes.constEnd()) |
256 | ? QQmlPropertyCache::ConstPtr() |
257 | : propertyCacheForPotentialInlineComponentType(t, iter); |
258 | } |
259 | |
260 | QT_END_NAMESPACE |
261 | |