| 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 "qqml.h" |
| 41 | |
| 42 | #include <QtQml/qqmlprivate.h> |
| 43 | |
| 44 | #include <private/qqmlengine_p.h> |
| 45 | #include <private/qqmlmetatype_p.h> |
| 46 | #include <private/qqmlmetatypedata_p.h> |
| 47 | #include <private/qqmltype_p_p.h> |
| 48 | #include <private/qqmltypemodule_p_p.h> |
| 49 | #include <private/qqmltypenotavailable_p.h> |
| 50 | |
| 51 | #include <QtCore/qmutex.h> |
| 52 | |
| 53 | QT_BEGIN_NAMESPACE |
| 54 | |
| 55 | void qmlClearTypeRegistrations() // Declared in qqml.h |
| 56 | { |
| 57 | QQmlMetaType::clearTypeRegistrations(); |
| 58 | QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types |
| 59 | qmlClearEnginePlugins(); |
| 60 | } |
| 61 | |
| 62 | //From qqml.h |
| 63 | bool qmlProtectModule(const char *uri, int majVersion) |
| 64 | { |
| 65 | return QQmlMetaType::protectModule(uri: QString::fromUtf8(str: uri), majVersion); |
| 66 | } |
| 67 | |
| 68 | //From qqml.h |
| 69 | void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) |
| 70 | { |
| 71 | QQmlMetaType::registerModule(uri, versionMajor, versionMinor); |
| 72 | } |
| 73 | |
| 74 | //From qqml.h |
| 75 | int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) |
| 76 | { |
| 77 | return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName); |
| 78 | } |
| 79 | |
| 80 | // From qqmlprivate.h |
| 81 | QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *) |
| 82 | { |
| 83 | if (!m_object) { |
| 84 | QQmlError error; |
| 85 | error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine." )); |
| 86 | QQmlEnginePrivate::get(e: qeng)->warning(qeng, error); |
| 87 | return nullptr; |
| 88 | } |
| 89 | |
| 90 | if (qeng->thread() != m_object->thread()) { |
| 91 | QQmlError error; |
| 92 | error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with" )); |
| 93 | QQmlEnginePrivate::get(e: qeng)->warning(qeng, error); |
| 94 | return nullptr; |
| 95 | } |
| 96 | if (alreadyCalled) { |
| 97 | QQmlError error; |
| 98 | error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine" )); |
| 99 | QQmlEnginePrivate::get(e: qeng)->warning(qeng, error); |
| 100 | return nullptr; |
| 101 | } |
| 102 | alreadyCalled = true; |
| 103 | qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership); |
| 104 | return m_object; |
| 105 | }; |
| 106 | |
| 107 | static QVector<int> availableRevisions(const QMetaObject *metaObject) |
| 108 | { |
| 109 | QVector<int> revisions; |
| 110 | if (!metaObject) |
| 111 | return revisions; |
| 112 | const int propertyOffset = metaObject->propertyOffset(); |
| 113 | const int propertyCount = metaObject->propertyCount(); |
| 114 | for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount; |
| 115 | propertyIndex < propertyEnd; ++propertyIndex) { |
| 116 | const QMetaProperty property = metaObject->property(index: propertyIndex); |
| 117 | if (int revision = property.revision()) |
| 118 | revisions.append(t: revision); |
| 119 | } |
| 120 | const int methodOffset = metaObject->methodOffset(); |
| 121 | const int methodCount = metaObject->methodCount(); |
| 122 | for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount; |
| 123 | methodIndex < methodEnd; ++methodIndex) { |
| 124 | const QMetaMethod method = metaObject->method(index: methodIndex); |
| 125 | if (int revision = method.revision()) |
| 126 | revisions.append(t: revision); |
| 127 | } |
| 128 | |
| 129 | // Need to also check parent meta objects, as their revisions are inherited. |
| 130 | if (const QMetaObject *superMeta = metaObject->superClass()) |
| 131 | revisions += availableRevisions(metaObject: superMeta); |
| 132 | |
| 133 | return revisions; |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | This method is "over generalized" to allow us to (potentially) register more types of things in |
| 138 | the future without adding exported symbols. |
| 139 | */ |
| 140 | int QQmlPrivate::qmlregister(RegistrationType type, void *data) |
| 141 | { |
| 142 | QQmlType dtype; |
| 143 | switch (type) { |
| 144 | case AutoParentRegistration: |
| 145 | return QQmlMetaType::registerAutoParentFunction( |
| 146 | autoparent: *reinterpret_cast<RegisterAutoParent *>(data)); |
| 147 | case QmlUnitCacheHookRegistration: |
| 148 | return QQmlMetaType::registerUnitCacheHook( |
| 149 | hookRegistration: *reinterpret_cast<RegisterQmlUnitCacheHook *>(data)); |
| 150 | case TypeAndRevisionsRegistration: { |
| 151 | const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data); |
| 152 | const char *elementName = classElementName(metaObject: type.classInfoMetaObject); |
| 153 | const bool creatable = (elementName != nullptr) |
| 154 | && boolClassInfo(metaObject: type.classInfoMetaObject, key: "QML.Creatable" , defaultValue: true); |
| 155 | |
| 156 | const QString noCreateReason = creatable |
| 157 | ? QString() |
| 158 | : QString::fromUtf8(str: classInfo(metaObject: type.classInfoMetaObject, key: "QML.UncreatableReason" )); |
| 159 | RegisterType revisionRegistration = { |
| 160 | .version: 1, |
| 161 | .typeId: type.typeId, |
| 162 | .listId: type.listId, |
| 163 | .objectSize: creatable ? type.objectSize : 0, |
| 164 | .create: nullptr, |
| 165 | .noCreationReason: noCreateReason, |
| 166 | .uri: type.uri, |
| 167 | .versionMajor: type.versionMajor, |
| 168 | .versionMinor: -1, |
| 169 | .elementName: nullptr, |
| 170 | .metaObject: type.metaObject, |
| 171 | .attachedPropertiesFunction: type.attachedPropertiesFunction, |
| 172 | .attachedPropertiesMetaObject: type.attachedPropertiesMetaObject, |
| 173 | .parserStatusCast: type.parserStatusCast, |
| 174 | .valueSourceCast: type.valueSourceCast, |
| 175 | .valueInterceptorCast: type.valueInterceptorCast, |
| 176 | .extensionObjectCreate: type.extensionObjectCreate, |
| 177 | .extensionMetaObject: type.extensionMetaObject, |
| 178 | .customParser: nullptr, |
| 179 | .revision: -1 |
| 180 | }; |
| 181 | |
| 182 | const int added = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.AddedInMinorVersion" , defaultValue: 0); |
| 183 | const int removed = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.RemovedInMinorVersion" , defaultValue: -1); |
| 184 | |
| 185 | auto revisions = availableRevisions(metaObject: type.metaObject); |
| 186 | revisions.append(t: qMax(a: added, b: 0)); |
| 187 | if (type.attachedPropertiesMetaObject) |
| 188 | revisions += availableRevisions(metaObject: type.attachedPropertiesMetaObject); |
| 189 | |
| 190 | std::sort(first: revisions.begin(), last: revisions.end()); |
| 191 | const auto it = std::unique(first: revisions.begin(), last: revisions.end()); |
| 192 | revisions.erase(abegin: it, aend: revisions.end()); |
| 193 | |
| 194 | const bool typeWasRemoved = removed >= added; |
| 195 | for (int revision : revisions) { |
| 196 | if (revision < added) |
| 197 | continue; |
| 198 | |
| 199 | // When removed, we still add revisions, but anonymous ones |
| 200 | if (typeWasRemoved && revision >= removed) { |
| 201 | revisionRegistration.elementName = nullptr; |
| 202 | revisionRegistration.create = nullptr; |
| 203 | } else { |
| 204 | revisionRegistration.elementName = elementName; |
| 205 | revisionRegistration.create = creatable ? type.create : nullptr; |
| 206 | } |
| 207 | |
| 208 | // Equivalent of qmlRegisterRevision<T, revision>(...) |
| 209 | revisionRegistration.versionMinor = revision; |
| 210 | revisionRegistration.revision = revision; |
| 211 | revisionRegistration.customParser = type.customParserFactory(); |
| 212 | |
| 213 | qmlregister(type: TypeRegistration, data: &revisionRegistration); |
| 214 | } |
| 215 | break; |
| 216 | } |
| 217 | case SingletonAndRevisionsRegistration: { |
| 218 | const RegisterSingletonTypeAndRevisions &type |
| 219 | = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data); |
| 220 | const char *elementName = classElementName(metaObject: type.classInfoMetaObject); |
| 221 | RegisterSingletonType revisionRegistration = { |
| 222 | .version: QmlCurrentSingletonTypeRegistrationVersion, |
| 223 | .uri: type.uri, |
| 224 | .versionMajor: type.versionMajor, |
| 225 | .versionMinor: -1, |
| 226 | .typeName: elementName, |
| 227 | |
| 228 | .scriptApi: type.scriptApi, |
| 229 | .qobjectApi: nullptr, |
| 230 | .instanceMetaObject: type.instanceMetaObject, |
| 231 | .typeId: type.typeId, |
| 232 | .revision: -1, |
| 233 | |
| 234 | .generalizedQobjectApi: type.generalizedQobjectApi |
| 235 | }; |
| 236 | |
| 237 | const int added = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.AddedInMinorVersion" , defaultValue: 0); |
| 238 | const int removed = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.RemovedInMinorVersion" , defaultValue: -1); |
| 239 | |
| 240 | auto revisions = availableRevisions(metaObject: type.instanceMetaObject); |
| 241 | revisions.append(t: qMax(a: added, b: 0)); |
| 242 | |
| 243 | std::sort(first: revisions.begin(), last: revisions.end()); |
| 244 | const auto it = std::unique(first: revisions.begin(), last: revisions.end()); |
| 245 | revisions.erase(abegin: it, aend: revisions.end()); |
| 246 | |
| 247 | const bool typeWasRemoved = removed >= added; |
| 248 | for (int revision : qAsConst(t&: revisions)) { |
| 249 | if (revision < added) |
| 250 | continue; |
| 251 | |
| 252 | // When removed, we still add revisions, but anonymous ones |
| 253 | if (typeWasRemoved && revision >= removed) { |
| 254 | revisionRegistration.typeName = nullptr; |
| 255 | revisionRegistration.scriptApi = nullptr; |
| 256 | revisionRegistration.generalizedQobjectApi = nullptr; |
| 257 | } else { |
| 258 | revisionRegistration.typeName = elementName; |
| 259 | revisionRegistration.scriptApi = type.scriptApi; |
| 260 | revisionRegistration.generalizedQobjectApi = type.generalizedQobjectApi; |
| 261 | } |
| 262 | |
| 263 | // Equivalent of qmlRegisterRevision<T, revision>(...) |
| 264 | revisionRegistration.versionMinor = revision; |
| 265 | revisionRegistration.revision = revision; |
| 266 | |
| 267 | qmlregister(type: SingletonRegistration, data: &revisionRegistration); |
| 268 | } |
| 269 | break; |
| 270 | } |
| 271 | case TypeRegistration: |
| 272 | dtype = QQmlMetaType::registerType(type: *reinterpret_cast<RegisterType *>(data)); |
| 273 | break; |
| 274 | case InterfaceRegistration: |
| 275 | dtype = QQmlMetaType::registerInterface(type: *reinterpret_cast<RegisterInterface *>(data)); |
| 276 | break; |
| 277 | case SingletonRegistration: |
| 278 | dtype = QQmlMetaType::registerSingletonType(type: *reinterpret_cast<RegisterSingletonType *>(data)); |
| 279 | break; |
| 280 | case CompositeRegistration: |
| 281 | dtype = QQmlMetaType::registerCompositeType(type: *reinterpret_cast<RegisterCompositeType *>(data)); |
| 282 | break; |
| 283 | case CompositeSingletonRegistration: |
| 284 | dtype = QQmlMetaType::registerCompositeSingletonType(type: *reinterpret_cast<RegisterCompositeSingletonType *>(data)); |
| 285 | break; |
| 286 | default: |
| 287 | return -1; |
| 288 | } |
| 289 | |
| 290 | if (!dtype.isValid()) |
| 291 | return -1; |
| 292 | |
| 293 | QQmlMetaType::registerUndeletableType(dtype); |
| 294 | return dtype.index(); |
| 295 | } |
| 296 | |
| 297 | void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data) |
| 298 | { |
| 299 | switch (type) { |
| 300 | case AutoParentRegistration: |
| 301 | QQmlMetaType::unregisterAutoParentFunction(function: reinterpret_cast<AutoParentFunction>(data)); |
| 302 | break; |
| 303 | case QmlUnitCacheHookRegistration: |
| 304 | QQmlMetaType::removeCachedUnitLookupFunction( |
| 305 | handler: reinterpret_cast<QmlUnitCacheLookupFunction>(data)); |
| 306 | break; |
| 307 | case TypeRegistration: |
| 308 | case InterfaceRegistration: |
| 309 | case SingletonRegistration: |
| 310 | case CompositeRegistration: |
| 311 | case CompositeSingletonRegistration: |
| 312 | QQmlMetaType::unregisterType(type: data); |
| 313 | break; |
| 314 | case TypeAndRevisionsRegistration: |
| 315 | case SingletonAndRevisionsRegistration: |
| 316 | // Currently unnecessary. We'd need a special data structure to hold |
| 317 | // URI + majorVersion and then we'd iterate the minor versions, look up the |
| 318 | // associated QQmlType objects by uri/elementName/major/minor and qmlunregister |
| 319 | // each of them. |
| 320 | Q_UNREACHABLE(); |
| 321 | break; |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | namespace QQmlPrivate { |
| 326 | template<> |
| 327 | void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>( |
| 328 | const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject) |
| 329 | { |
| 330 | using T = QQmlTypeNotAvailable; |
| 331 | |
| 332 | QML_GETTYPENAMES |
| 333 | |
| 334 | RegisterTypeAndRevisions type = { |
| 335 | .version: 0, |
| 336 | .typeId: qRegisterNormalizedMetaType<T *>(normalizedTypeName: pointerName.constData()), |
| 337 | .listId: qRegisterNormalizedMetaType<QQmlListProperty<T> >(normalizedTypeName: listName.constData()), |
| 338 | .objectSize: 0, |
| 339 | .create: nullptr, |
| 340 | |
| 341 | .uri: uri, |
| 342 | .versionMajor: versionMajor, |
| 343 | |
| 344 | .metaObject: &QQmlTypeNotAvailable::staticMetaObject, |
| 345 | .classInfoMetaObject: classInfoMetaObject, |
| 346 | |
| 347 | .attachedPropertiesFunction: attachedPropertiesFunc<T>(), |
| 348 | .attachedPropertiesMetaObject: attachedPropertiesMetaObject<T>(), |
| 349 | |
| 350 | .parserStatusCast: StaticCastSelector<T, QQmlParserStatus>::cast(), |
| 351 | .valueSourceCast: StaticCastSelector<T, QQmlPropertyValueSource>::cast(), |
| 352 | .valueInterceptorCast: StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(), |
| 353 | |
| 354 | .extensionObjectCreate: nullptr, .extensionMetaObject: nullptr, .customParserFactory: qmlCreateCustomParser<T> |
| 355 | }; |
| 356 | |
| 357 | qmlregister(type: TypeAndRevisionsRegistration, data: &type); |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | QT_END_NAMESPACE |
| 362 | |