| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2017 Ford Motor Company |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtRemoteObjects 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 | #ifndef QREMOTEOBJECTSOURCE_H |
| 41 | #define QREMOTEOBJECTSOURCE_H |
| 42 | |
| 43 | #include <QtCore/qscopedpointer.h> |
| 44 | #include <QtRemoteObjects/qtremoteobjectglobal.h> |
| 45 | #include <QtCore/qmetaobject.h> |
| 46 | |
| 47 | QT_BEGIN_NAMESPACE |
| 48 | |
| 49 | namespace QtPrivate { |
| 50 | |
| 51 | //Based on compile time checks for static connect() from qobjectdefs_impl.h |
| 52 | template <class ObjectType, typename Func1, typename Func2> |
| 53 | static inline int qtro_property_index(Func1, Func2, const char *propName) |
| 54 | { |
| 55 | typedef QtPrivate::FunctionPointer<Func1> Type1; |
| 56 | typedef QtPrivate::FunctionPointer<Func2> Type2; |
| 57 | |
| 58 | //compilation error if the arguments do not match. |
| 59 | Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount), |
| 60 | "Argument counts are not compatible." ); |
| 61 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value), |
| 62 | "Arguments are not compatible." ); |
| 63 | Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value), |
| 64 | "Return types are not compatible." ); |
| 65 | return ObjectType::staticMetaObject.indexOfProperty(propName); |
| 66 | } |
| 67 | |
| 68 | template <class ObjectType, typename Func1, typename Func2> |
| 69 | static inline int qtro_signal_index(Func1 func, Func2, int *count, int const **types) |
| 70 | { |
| 71 | typedef QtPrivate::FunctionPointer<Func1> Type1; |
| 72 | typedef QtPrivate::FunctionPointer<Func2> Type2; |
| 73 | |
| 74 | //compilation error if the arguments do not match. |
| 75 | Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount), |
| 76 | "Argument counts are not compatible." ); |
| 77 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value), |
| 78 | "Arguments are not compatible." ); |
| 79 | Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value), |
| 80 | "Return types are not compatible." ); |
| 81 | const QMetaMethod sig = QMetaMethod::fromSignal(func); |
| 82 | *count = Type2::ArgumentCount; |
| 83 | *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types(); |
| 84 | return sig.methodIndex(); |
| 85 | } |
| 86 | |
| 87 | template <class ObjectType, typename Func1, typename Func2> |
| 88 | static inline void qtro_method_test(Func1, Func2) |
| 89 | { |
| 90 | typedef QtPrivate::FunctionPointer<Func1> Type1; |
| 91 | typedef QtPrivate::FunctionPointer<Func2> Type2; |
| 92 | |
| 93 | //compilation error if the arguments do not match. |
| 94 | Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount), |
| 95 | "Argument counts are not compatible." ); |
| 96 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value), |
| 97 | "Arguments are not compatible." ); |
| 98 | Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value), |
| 99 | "Return types are not compatible." ); |
| 100 | } |
| 101 | |
| 102 | // The stringData, methodMatch and QMetaObjectPrivate methods are modified versions of the code |
| 103 | // from qmetaobject_p.h/qmetaobject.cpp. The modifications are based on our custom need to match |
| 104 | // a method name that comes from the .rep file. |
| 105 | // The QMetaObjectPrivate struct should only have members appended to maintain binary compatibility, |
| 106 | // so we should be fine with only the listed version with the fields we use. |
| 107 | inline const QByteArray apiStringData(const QMetaObject *mo, int index) |
| 108 | { |
| 109 | const QByteArrayDataPtr data = { .ptr: const_cast<QByteArrayData*>(&mo->d.stringdata[index]) }; |
| 110 | return data; |
| 111 | } |
| 112 | |
| 113 | inline bool apiMethodMatch(const QMetaObject *m, int handle, |
| 114 | const QByteArray &name, int argc, |
| 115 | const int *types) |
| 116 | { |
| 117 | if (int(m->d.data[handle + 1]) != argc) |
| 118 | return false; |
| 119 | if (apiStringData(mo: m, index: m->d.data[handle]) != name) |
| 120 | return false; |
| 121 | int paramsIndex = m->d.data[handle + 2] + 1; |
| 122 | for (int i = 0; i < argc; ++i) { |
| 123 | uint typeInfo = m->d.data[paramsIndex + i]; |
| 124 | if (typeInfo & 0x80000000) { // Custom/named type, compare names |
| 125 | const char *t = QMetaType::typeName(type: types[i]); |
| 126 | const auto type = QByteArray::fromRawData(t, size: qstrlen(str: t)); |
| 127 | if (type != apiStringData(mo: m, index: typeInfo & 0x7FFFFFFF)) |
| 128 | return false; |
| 129 | } else if (types[i] != int(typeInfo)) |
| 130 | return false; |
| 131 | } |
| 132 | return true; |
| 133 | } |
| 134 | |
| 135 | struct QMetaObjectPrivate |
| 136 | { |
| 137 | // revision 7 is Qt 5.0 everything lower is not supported |
| 138 | // revision 8 is Qt 5.12: It adds the enum name to QMetaEnum |
| 139 | enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus |
| 140 | |
| 141 | int revision; |
| 142 | int className; |
| 143 | int classInfoCount, classInfoData; |
| 144 | int methodCount, methodData; |
| 145 | int propertyCount, propertyData; |
| 146 | int enumeratorCount, enumeratorData; |
| 147 | int constructorCount, constructorData; |
| 148 | int flags; |
| 149 | int signalCount; |
| 150 | }; |
| 151 | |
| 152 | template <class ObjectType, typename Func1, typename Func2> |
| 153 | static inline int qtro_method_index(Func1, Func2, const char *methodName, int *count, int const **types) |
| 154 | { |
| 155 | typedef QtPrivate::FunctionPointer<Func1> Type1; |
| 156 | typedef QtPrivate::FunctionPointer<Func2> Type2; |
| 157 | |
| 158 | //compilation error if the arguments do not match. |
| 159 | Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount), |
| 160 | "Argument counts are not compatible." ); |
| 161 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value), |
| 162 | "Arguments are not compatible." ); |
| 163 | Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value), |
| 164 | "Return types are not compatible." ); |
| 165 | *count = Type2::ArgumentCount; |
| 166 | *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types(); |
| 167 | |
| 168 | int result = ObjectType::staticMetaObject.indexOfMethod(methodName); |
| 169 | if (result >= 0) |
| 170 | return result; |
| 171 | // We can have issues, specifically with enums, since the compiler can infer the class. Since |
| 172 | // indexOfMethod() is doing string comparisons for registered types, "MyEnum" and "MyClass::MyEnum" |
| 173 | // won't match. |
| 174 | // Below is similar to QMetaObject->indexOfMethod, but template magic has already matched parameter |
| 175 | // types, so we need to find a match for the API method name + parameters. Neither approach works |
| 176 | // 100%, as the below code doesn't match a parameter of type "size_t" (which the template match |
| 177 | // identifies as "ulong"). These subtleties can cause the below string comparison fails. |
| 178 | // There is no known case that would fail both methods. |
| 179 | // TODO: is there a way to make this a constexpr so a failure is detected at compile time? |
| 180 | int nameLength = strchr(s: methodName, c: '(') - methodName; |
| 181 | const auto name = QByteArray::fromRawData(methodName, size: nameLength); |
| 182 | for (const QMetaObject *m = &ObjectType::staticMetaObject; m; m = m->d.superdata) { |
| 183 | const auto priv = reinterpret_cast<const QMetaObjectPrivate*>(m->d.data); |
| 184 | int i = (priv->methodCount - 1); |
| 185 | const int end = priv->signalCount; |
| 186 | for (; i >= end; --i) { |
| 187 | int handle = priv->methodData + 5*i; |
| 188 | if (apiMethodMatch(m, handle, name, argc: *count, types: *types)) |
| 189 | return i + m->methodOffset(); |
| 190 | } |
| 191 | } |
| 192 | qWarning() << "No matching method for" << methodName << "in the provided metaclass" << ObjectType::staticMetaObject.className(); |
| 193 | return -1; |
| 194 | } |
| 195 | |
| 196 | template <class ObjectType> |
| 197 | static inline QByteArray qtro_enum_signature(const char *enumName) |
| 198 | { |
| 199 | const auto qme = ObjectType::staticMetaObject.enumerator(ObjectType::staticMetaObject.indexOfEnumerator(enumName)); |
| 200 | return QByteArrayLiteral("1::2" ).replace("1" , qme.scope()).replace("2" , qme.name()); |
| 201 | } |
| 202 | |
| 203 | QByteArray qtro_classinfo_signature(const QMetaObject *metaObject); |
| 204 | |
| 205 | } |
| 206 | |
| 207 | // TODO ModelInfo just needs roles, and no need for SubclassInfo |
| 208 | class QAbstractItemModel; |
| 209 | |
| 210 | struct ModelInfo |
| 211 | { |
| 212 | QAbstractItemModel *ptr; |
| 213 | QString name; |
| 214 | QByteArray roles; |
| 215 | }; |
| 216 | |
| 217 | class SourceApiMap |
| 218 | { |
| 219 | protected: |
| 220 | SourceApiMap() {} |
| 221 | public: |
| 222 | virtual ~SourceApiMap() {} |
| 223 | virtual QString name() const = 0; |
| 224 | virtual QString typeName() const = 0; |
| 225 | virtual QByteArray className() const { return typeName().toLatin1().append(s: "Source" ); } |
| 226 | virtual int enumCount() const = 0; |
| 227 | virtual int propertyCount() const = 0; |
| 228 | virtual int signalCount() const = 0; |
| 229 | virtual int methodCount() const = 0; |
| 230 | virtual int sourceEnumIndex(int index) const = 0; |
| 231 | virtual int sourcePropertyIndex(int index) const = 0; |
| 232 | virtual int sourceSignalIndex(int index) const = 0; |
| 233 | virtual int sourceMethodIndex(int index) const = 0; |
| 234 | virtual int signalParameterCount(int index) const = 0; |
| 235 | virtual int signalParameterType(int sigIndex, int paramIndex) const = 0; |
| 236 | virtual const QByteArray signalSignature(int index) const = 0; |
| 237 | virtual QList<QByteArray> signalParameterNames(int index) const = 0; |
| 238 | virtual int methodParameterCount(int index) const = 0; |
| 239 | virtual int methodParameterType(int methodIndex, int paramIndex) const = 0; |
| 240 | virtual const QByteArray methodSignature(int index) const = 0; |
| 241 | virtual QMetaMethod::MethodType methodType(int index) const = 0; |
| 242 | virtual const QByteArray typeName(int index) const = 0; |
| 243 | virtual QList<QByteArray> methodParameterNames(int index) const = 0; |
| 244 | virtual int propertyIndexFromSignal(int index) const = 0; |
| 245 | virtual int propertyRawIndexFromSignal(int index) const = 0; |
| 246 | virtual QByteArray objectSignature() const = 0; |
| 247 | virtual bool isDynamic() const { return false; } |
| 248 | virtual bool isAdapterSignal(int) const { return false; } |
| 249 | virtual bool isAdapterMethod(int) const { return false; } |
| 250 | virtual bool isAdapterProperty(int) const { return false; } |
| 251 | QVector<ModelInfo> m_models; |
| 252 | QVector<SourceApiMap *> m_subclasses; |
| 253 | }; |
| 254 | |
| 255 | QT_END_NAMESPACE |
| 256 | |
| 257 | #endif |
| 258 | |