1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlmetatypedata_p.h"
41
42#include <private/qqmltype_p_p.h>
43#include <private/qqmltypemodule_p.h>
44#include <private/qqmlpropertycache_p.h>
45
46QT_BEGIN_NAMESPACE
47
48QQmlMetaTypeData::QQmlMetaTypeData()
49{
50}
51
52QQmlMetaTypeData::~QQmlMetaTypeData()
53{
54 for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i)
55 delete *i;
56 for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end();
57 it != end; ++it)
58 (*it)->release();
59
60 // Do this before the attached properties disappear.
61 types.clear();
62 undeletableTypes.clear();
63}
64
65// This expects a "fresh" QQmlTypePrivate and adopts its reference.
66void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
67{
68 for (int i = 0; i < types.count(); ++i) {
69 if (!types.at(i).isValid()) {
70 types[i] = QQmlType(priv);
71 priv->index = i;
72 return;
73 }
74 }
75 types.append(QQmlType(priv));
76 priv->index = types.count() - 1;
77 priv->release();
78}
79
80QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const
81{
82 return (index < typePropertyCaches.length())
83 ? typePropertyCaches.at(index).value(minorVersion).data()
84 : nullptr;
85}
86
87void QQmlMetaTypeData::setPropertyCacheForMinorVersion(int index, int minorVersion,
88 QQmlPropertyCache *cache)
89{
90 if (index >= typePropertyCaches.length())
91 typePropertyCaches.resize(index + 1);
92 typePropertyCaches[index][minorVersion] = cache;
93}
94
95void QQmlMetaTypeData::clearPropertyCachesForMinorVersion(int index)
96{
97 if (index < typePropertyCaches.length())
98 typePropertyCaches[index].clear();
99}
100
101QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion)
102{
103 if (QQmlPropertyCache *rv = propertyCaches.value(metaObject))
104 return rv;
105
106 if (!metaObject->superClass()) {
107 QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject);
108 propertyCaches.insert(metaObject, rv);
109 return rv;
110 }
111 QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion);
112 QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion);
113 propertyCaches.insert(metaObject, rv);
114 return rv;
115}
116
117QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion)
118{
119 Q_ASSERT(type.isValid());
120
121 if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion))
122 return pc;
123
124 QVector<QQmlType> types;
125
126 int maxMinorVersion = 0;
127
128 const QMetaObject *metaObject = type.metaObject();
129
130 while (metaObject) {
131 QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion);
132 if (t.isValid()) {
133 maxMinorVersion = qMax(maxMinorVersion, t.minorVersion());
134 types << t;
135 } else {
136 types << QQmlType();
137 }
138
139 metaObject = metaObject->superClass();
140 }
141
142 if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) {
143 setPropertyCacheForMinorVersion(type.index(), minorVersion, pc);
144 return pc;
145 }
146
147 QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion);
148
149 bool hasCopied = false;
150
151 for (int ii = 0; ii < types.count(); ++ii) {
152 QQmlType currentType = types.at(ii);
153 if (!currentType.isValid())
154 continue;
155
156 int rev = currentType.metaObjectRevision();
157 int moIndex = types.count() - 1 - ii;
158
159 if (raw->allowedRevision(moIndex) != rev) {
160 if (!hasCopied) {
161 // TODO: The copy should be mutable, and the original should be const
162 // Considering this, the setAllowedRevision() below does not violate
163 // the immutability of already published property caches.
164 raw = raw->copy();
165 hasCopied = true;
166 }
167 raw->setAllowedRevision(moIndex, rev);
168 }
169 }
170
171 // Test revision compatibility - the basic rule is:
172 // * Anything that is excluded, cannot overload something that is not excluded *
173
174 // Signals override:
175 // * other signals and methods of the same name.
176 // * properties named on<Signal Name>
177 // * automatic <property name>Changed notify signals
178
179 // Methods override:
180 // * other methods of the same name
181
182 // Properties override:
183 // * other elements of the same name
184
185#if 0
186 bool overloadError = false;
187 QString overloadName;
188
189 for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin();
190 !overloadError && iter != raw->stringCache.end();
191 ++iter) {
192
193 QQmlPropertyData *d = *iter;
194 if (raw->isAllowedInRevision(d))
195 continue; // Not excluded - no problems
196
197 // check that a regular "name" overload isn't happening
198 QQmlPropertyData *current = d;
199 while (!overloadError && current) {
200 current = d->overrideData(current);
201 if (current && raw->isAllowedInRevision(current))
202 overloadError = true;
203 }
204 }
205
206 if (overloadError) {
207 if (hasCopied) raw->release();
208
209 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."));
210 return 0;
211 }
212#endif
213
214 setPropertyCacheForMinorVersion(type.index(), minorVersion, raw);
215
216 if (hasCopied)
217 raw->release();
218
219 if (minorVersion != maxMinorVersion)
220 setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw);
221
222 return raw;
223}
224
225QT_END_NAMESPACE
226