1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "fakemetaobject.h"
27 #include <QCryptographicHash>
28 
29 using namespace LanguageUtils;
30 
FakeMetaEnum()31 FakeMetaEnum::FakeMetaEnum()
32 {}
33 
FakeMetaEnum(const QString & name)34 FakeMetaEnum::FakeMetaEnum(const QString &name)
35     : m_name(name)
36 {}
37 
isValid() const38 bool FakeMetaEnum::isValid() const
39 { return !m_name.isEmpty(); }
40 
name() const41 QString FakeMetaEnum::name() const
42 { return m_name; }
43 
setName(const QString & name)44 void FakeMetaEnum::setName(const QString &name)
45 { m_name = name; }
46 
addKey(const QString & key)47 void FakeMetaEnum::addKey(const QString &key)
48 { m_keys.append(key); }
49 
key(int index) const50 QString FakeMetaEnum::key(int index) const
51 { return m_keys.at(index); }
52 
keyCount() const53 int FakeMetaEnum::keyCount() const
54 { return m_keys.size(); }
55 
keys() const56 QStringList FakeMetaEnum::keys() const
57 { return m_keys; }
58 
hasKey(const QString & key) const59 bool FakeMetaEnum::hasKey(const QString &key) const
60 { return m_keys.contains(key); }
61 
addToHash(QCryptographicHash & hash) const62 void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
63 {
64     int len = m_name.size();
65     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
66     hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
67     len = m_keys.size();
68     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
69     foreach (const QString &key, m_keys) {
70         len = key.size();
71         hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
72         hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
73     }
74 }
75 
describe(int baseIndent) const76 QString FakeMetaEnum::describe(int baseIndent) const
77 {
78     QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
79     QString res = QLatin1String("Enum ");
80     res += name();
81     res += QLatin1String(": [");
82     for (int i = 0; i < keyCount(); ++i) {
83         res += newLine;
84         res += QLatin1String("  ");
85         res += key(i);
86     }
87     res += newLine;
88     res += QLatin1Char(']');
89     return res;
90 }
91 
toString() const92 QString FakeMetaEnum::toString() const
93 {
94     return describe();
95 }
96 
FakeMetaMethod(const QString & name,const QString & returnType)97 FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType)
98     : m_name(name)
99     , m_returnType(returnType)
100     , m_methodTy(FakeMetaMethod::Method)
101     , m_methodAccess(FakeMetaMethod::Public)
102     , m_revision(0)
103 {}
104 
FakeMetaMethod()105 FakeMetaMethod::FakeMetaMethod()
106     : m_methodTy(FakeMetaMethod::Method)
107     , m_methodAccess(FakeMetaMethod::Public)
108     , m_revision(0)
109 {}
110 
methodName() const111 QString FakeMetaMethod::methodName() const
112 { return m_name; }
113 
setMethodName(const QString & name)114 void FakeMetaMethod::setMethodName(const QString &name)
115 { m_name = name; }
116 
setReturnType(const QString & type)117 void FakeMetaMethod::setReturnType(const QString &type)
118 { m_returnType = type; }
119 
parameterNames() const120 QStringList FakeMetaMethod::parameterNames() const
121 { return m_paramNames; }
122 
parameterTypes() const123 QStringList FakeMetaMethod::parameterTypes() const
124 { return m_paramTypes; }
125 
addParameter(const QString & name,const QString & type)126 void FakeMetaMethod::addParameter(const QString &name, const QString &type)
127 { m_paramNames.append(name); m_paramTypes.append(type); }
128 
methodType() const129 int FakeMetaMethod::methodType() const
130 { return m_methodTy; }
131 
setMethodType(int methodType)132 void FakeMetaMethod::setMethodType(int methodType)
133 { m_methodTy = methodType; }
134 
access() const135 int FakeMetaMethod::access() const
136 { return m_methodAccess; }
137 
revision() const138 int FakeMetaMethod::revision() const
139 { return m_revision; }
140 
setRevision(int r)141 void FakeMetaMethod::setRevision(int r)
142 { m_revision = r; }
143 
addToHash(QCryptographicHash & hash) const144 void FakeMetaMethod::addToHash(QCryptographicHash &hash) const
145 {
146     int len = m_name.size();
147     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
148     hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
149     hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess));
150     hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy));
151     hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
152     len = m_paramNames.size();
153     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
154     foreach (const QString &pName, m_paramNames) {
155         len = pName.size();
156         hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
157         hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar));
158     }
159     len = m_paramTypes.size();
160     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
161     foreach (const QString &pType, m_paramTypes) {
162         len = pType.size();
163         hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
164         hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar));
165     }
166     len = m_returnType.size();
167     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
168     hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar));
169 }
170 
describe(int baseIndent) const171 QString FakeMetaMethod::describe(int baseIndent) const
172 {
173     QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
174     QString res = QLatin1String("Method {");
175     res += newLine;
176     res += QLatin1String("  methodName:");
177     res += methodName();
178     res += newLine;
179     res += QLatin1String("  methodType:");
180     res += QString::number(methodType());
181     res += newLine;
182     res += QLatin1String("  parameterNames:[");
183     foreach (const QString &pName, parameterNames()) {
184         res += newLine;
185         res += QLatin1String("    ");
186         res += pName;
187     }
188     res += QLatin1Char(']');
189     res += newLine;
190     res += QLatin1String("  parameterTypes:[");
191     foreach (const QString &pType, parameterTypes()) {
192         res += newLine;
193         res += QLatin1String("    ");
194         res += pType;
195     }
196     res += QLatin1Char(']');
197     res += newLine;
198     res += QLatin1Char('}');
199     return res;
200 }
201 
toString() const202 QString FakeMetaMethod::toString() const
203 {
204     return describe();
205 }
206 
207 
FakeMetaProperty(const QString & name,const QString & type,bool isList,bool isWritable,bool isPointer,int revision)208 FakeMetaProperty::FakeMetaProperty(const QString &name, const QString &type, bool isList,
209                                    bool isWritable, bool isPointer, int revision)
210     : m_propertyName(name)
211     , m_type(type)
212     , m_isList(isList)
213     , m_isWritable(isWritable)
214     , m_isPointer(isPointer)
215     , m_revision(revision)
216 {}
217 
name() const218 QString FakeMetaProperty::name() const
219 { return m_propertyName; }
220 
typeName() const221 QString FakeMetaProperty::typeName() const
222 { return m_type; }
223 
isList() const224 bool FakeMetaProperty::isList() const
225 { return m_isList; }
226 
isWritable() const227 bool FakeMetaProperty::isWritable() const
228 { return m_isWritable; }
229 
isPointer() const230 bool FakeMetaProperty::isPointer() const
231 { return m_isPointer; }
232 
revision() const233 int FakeMetaProperty::revision() const
234 { return m_revision; }
235 
addToHash(QCryptographicHash & hash) const236 void FakeMetaProperty::addToHash(QCryptographicHash &hash) const
237 {
238     int len = m_propertyName.size();
239     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
240     hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar));
241     hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
242     int flags = (m_isList ? (1 << 0) : 0)
243             + (m_isPointer ? (1 << 1) : 0)
244             + (m_isWritable ? (1 << 2) : 0);
245     hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
246     len = m_type.size();
247     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
248     hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar));
249 }
250 
describe(int baseIndent) const251 QString FakeMetaProperty::describe(int baseIndent) const
252 {
253     auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
254     QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
255     QString res = QLatin1String("Property  {");
256     res += newLine;
257     res += QLatin1String("  name:");
258     res += name();
259     res += newLine;
260     res += QLatin1String("  typeName:");
261     res += typeName();
262     res += newLine;
263     res += QLatin1String("  typeName:");
264     res += QString::number(revision());
265     res += newLine;
266     res += QLatin1String("  isList:");
267     res += boolStr(isList());
268     res += newLine;
269     res += QLatin1String("  isPointer:");
270     res += boolStr(isPointer());
271     res += newLine;
272     res += QLatin1String("  isWritable:");
273     res += boolStr(isWritable());
274     res += newLine;
275     res += QLatin1Char('}');
276     return res;
277 }
278 
toString() const279 QString FakeMetaProperty::toString() const
280 {
281     return describe();
282 }
283 
284 
FakeMetaObject()285 FakeMetaObject::FakeMetaObject() : m_isSingleton(false), m_isCreatable(true), m_isComposite(false)
286 {
287 }
288 
className() const289 QString FakeMetaObject::className() const
290 { return m_className; }
setClassName(const QString & name)291 void FakeMetaObject::setClassName(const QString &name)
292 { m_className = name; }
293 
addExport(const QString & name,const QString & package,ComponentVersion version)294 void FakeMetaObject::addExport(const QString &name, const QString &package, ComponentVersion version)
295 {
296     Export exp;
297     exp.type = name;
298     exp.package = package;
299     exp.version = version;
300     m_exports.append(exp);
301 }
302 
setExportMetaObjectRevision(int exportIndex,int metaObjectRevision)303 void FakeMetaObject::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
304 {
305     m_exports[exportIndex].metaObjectRevision = metaObjectRevision;
306 }
307 
exports() const308 QList<FakeMetaObject::Export> FakeMetaObject::exports() const
309 { return m_exports; }
exportInPackage(const QString & package) const310 FakeMetaObject::Export FakeMetaObject::exportInPackage(const QString &package) const
311 {
312     foreach (const Export &exp, m_exports) {
313         if (exp.package == package)
314             return exp;
315     }
316     return Export();
317 }
318 
setSuperclassName(const QString & superclass)319 void FakeMetaObject::setSuperclassName(const QString &superclass)
320 { m_superName = superclass; }
superclassName() const321 QString FakeMetaObject::superclassName() const
322 { return m_superName; }
323 
addEnum(const FakeMetaEnum & fakeEnum)324 void FakeMetaObject::addEnum(const FakeMetaEnum &fakeEnum)
325 { m_enumNameToIndex.insert(fakeEnum.name(), m_enums.size()); m_enums.append(fakeEnum); }
enumeratorCount() const326 int FakeMetaObject::enumeratorCount() const
327 { return m_enums.size(); }
enumeratorOffset() const328 int FakeMetaObject::enumeratorOffset() const
329 { return 0; }
enumerator(int index) const330 FakeMetaEnum FakeMetaObject::enumerator(int index) const
331 { return m_enums.at(index); }
enumeratorIndex(const QString & name) const332 int FakeMetaObject::enumeratorIndex(const QString &name) const
333 { return m_enumNameToIndex.value(name, -1); }
334 
addProperty(const FakeMetaProperty & property)335 void FakeMetaObject::addProperty(const FakeMetaProperty &property)
336 { m_propNameToIdx.insert(property.name(), m_props.size()); m_props.append(property); }
propertyCount() const337 int FakeMetaObject::propertyCount() const
338 { return m_props.size(); }
propertyOffset() const339 int FakeMetaObject::propertyOffset() const
340 { return 0; }
property(int index) const341 FakeMetaProperty FakeMetaObject::property(int index) const
342 { return m_props.at(index); }
propertyIndex(const QString & name) const343 int FakeMetaObject::propertyIndex(const QString &name) const
344 { return m_propNameToIdx.value(name, -1); }
345 
addMethod(const FakeMetaMethod & method)346 void FakeMetaObject::addMethod(const FakeMetaMethod &method)
347 { m_methods.append(method); }
methodCount() const348 int FakeMetaObject::methodCount() const
349 { return m_methods.size(); }
methodOffset() const350 int FakeMetaObject::methodOffset() const
351 { return 0; }
method(int index) const352 FakeMetaMethod FakeMetaObject::method(int index) const
353 { return m_methods.at(index); }
methodIndex(const QString & name) const354 int FakeMetaObject::methodIndex(const QString &name) const //If performances becomes an issue, just use a nameToIdx hash
355 {
356     for (int i=0; i<m_methods.count(); i++)
357         if (m_methods[i].methodName() == name)
358             return i;
359     return -1;
360 }
361 
defaultPropertyName() const362 QString FakeMetaObject::defaultPropertyName() const
363 { return m_defaultPropertyName; }
setDefaultPropertyName(const QString & defaultPropertyName)364 void FakeMetaObject::setDefaultPropertyName(const QString &defaultPropertyName)
365 { m_defaultPropertyName = defaultPropertyName; }
366 
attachedTypeName() const367 QString FakeMetaObject::attachedTypeName() const
368 { return m_attachedTypeName; }
setAttachedTypeName(const QString & name)369 void FakeMetaObject::setAttachedTypeName(const QString &name)
370 { m_attachedTypeName = name; }
371 
extensionTypeName() const372 QString FakeMetaObject::extensionTypeName() const
373 { return m_extensionTypeName; }
setExtensionTypeName(const QString & name)374 void FakeMetaObject::setExtensionTypeName(const QString &name)
375 { m_extensionTypeName = name; }
376 
calculateFingerprint() const377 QByteArray FakeMetaObject::calculateFingerprint() const
378 {
379     QCryptographicHash hash(QCryptographicHash::Sha1);
380     int len = m_className.size();
381     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
382     hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar));
383     len = m_attachedTypeName.size();
384     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
385     hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar));
386     len = m_defaultPropertyName.size();
387     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
388     hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar));
389     len = m_enumNameToIndex.size();
390     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
391     {
392         QStringList keys(m_enumNameToIndex.keys());
393         keys.sort();
394         foreach (const QString &key, keys) {
395             len = key.size();
396             hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
397             hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
398             int value = m_enumNameToIndex.value(key);
399             hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
400             m_enums.at(value).addToHash(hash);
401         }
402     }
403     len = m_exports.size();
404     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
405     foreach (const Export &e, m_exports)
406         e.addToHash(hash); // normalize order?
407     len = m_exports.size();
408     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
409     foreach (const FakeMetaMethod &m, m_methods)
410         m.addToHash(hash); // normalize order?
411     {
412         QStringList keys(m_propNameToIdx.keys());
413         keys.sort();
414         foreach (const QString &key, keys) {
415             len = key.size();
416             hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
417             hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
418             int value = m_propNameToIdx.value(key);
419             hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
420             m_props.at(value).addToHash(hash);
421         }
422     }
423     len = m_superName.size();
424     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
425     hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar));
426 
427     QByteArray res = hash.result();
428     res.append('F');
429     return res;
430 }
431 
updateFingerprint()432 void FakeMetaObject::updateFingerprint()
433 {
434     m_fingerprint = calculateFingerprint();
435 }
436 
fingerprint() const437 QByteArray FakeMetaObject::fingerprint() const
438 {
439     return m_fingerprint;
440 }
441 
isSingleton() const442 bool FakeMetaObject::isSingleton() const
443 {
444     return m_isSingleton;
445 }
446 
isCreatable() const447 bool FakeMetaObject::isCreatable() const
448 {
449     return m_isCreatable;
450 }
451 
isComposite() const452 bool FakeMetaObject::isComposite() const
453 {
454     return m_isComposite;
455 }
456 
setIsSingleton(bool value)457 void FakeMetaObject::setIsSingleton(bool value)
458 {
459     m_isSingleton = value;
460 }
461 
setIsCreatable(bool value)462 void FakeMetaObject::setIsCreatable(bool value)
463 {
464     m_isCreatable = value;
465 }
466 
setIsComposite(bool value)467 void FakeMetaObject::setIsComposite(bool value)
468 {
469     m_isSingleton = value;
470 }
471 
toString() const472 QString FakeMetaObject::toString() const
473 {
474     return describe();
475 }
476 
describe(bool printDetails,int baseIndent) const477 QString FakeMetaObject::describe(bool printDetails, int baseIndent) const
478 {
479     QString res = QString::fromLatin1("FakeMetaObject@%1")
480          .arg((quintptr)(void *)this, 0, 16);
481     if (!printDetails)
482         return res;
483     auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
484     QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
485     res += QLatin1Char('{');
486     res += newLine;
487     res += QLatin1String("className:");
488     res += className();
489     res += newLine;
490     res += QLatin1String("superClassName:");
491     res += superclassName();
492     res += newLine;
493     res += QLatin1String("isSingleton:");
494     res += boolStr(isSingleton());
495     res += newLine;
496     res += QLatin1String("isCreatable:");
497     res += boolStr(isCreatable());
498     res += newLine;
499     res += QLatin1String("isComposite:");
500     res += boolStr(isComposite());
501     res += newLine;
502     res += QLatin1String("defaultPropertyName:");
503     res += defaultPropertyName();
504     res += newLine;
505     res += QLatin1String("attachedTypeName:");
506     res += attachedTypeName();
507     res += newLine;
508     res += QLatin1String("fingerprint:");
509     res += QString::fromUtf8(fingerprint());
510 
511     res += newLine;
512     res += QLatin1String("exports:[");
513     foreach (const Export &e, exports()) {
514         res += newLine;
515         res += QLatin1String("  ");
516         res += e.describe(baseIndent + 2);
517     }
518     res += QLatin1Char(']');
519 
520     res += newLine;
521     res += QLatin1String("enums:[");
522     for (int iEnum = 0; iEnum < enumeratorCount() ; ++ iEnum) {
523         FakeMetaEnum e = enumerator(enumeratorOffset() + iEnum);
524         res += newLine;
525         res += QLatin1String("  ");
526         res += e.describe(baseIndent + 2);
527     }
528     res += QLatin1Char(']');
529 
530     res += newLine;
531     res += QLatin1String("properties:[");
532     for (int iProp = 0; iProp < propertyCount() ; ++ iProp) {
533         FakeMetaProperty prop = property(propertyOffset() + iProp);
534         res += newLine;
535         res += QLatin1String("  ");
536         res += prop.describe(baseIndent + 2);
537     }
538     res += QLatin1Char(']');
539     res += QLatin1String("methods:[");
540     for (int iMethod = 0; iMethod < methodOffset() ; ++ iMethod) {
541         FakeMetaMethod m = method(methodOffset() + iMethod);
542         res += newLine;
543         res += QLatin1String("  ");
544         m.describe(baseIndent + 2);
545     }
546     res += QLatin1Char(']');
547     res += newLine;
548     res += QLatin1Char('}');
549     return res;
550 }
551 
Export()552 FakeMetaObject::Export::Export()
553     : metaObjectRevision(0)
554 {}
isValid() const555 bool FakeMetaObject::Export::isValid() const
556 { return version.isValid() || !package.isEmpty() || !type.isEmpty(); }
557 
addToHash(QCryptographicHash & hash) const558 void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const
559 {
560     int len = package.size();
561     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
562     hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar));
563     len = type.size();
564     hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
565     hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar));
566     version.addToHash(hash);
567     hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision));
568 }
569 
describe(int baseIndent) const570 QString FakeMetaObject::Export::describe(int baseIndent) const
571 {
572     QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
573     QString res = QLatin1String("Export {");
574     res += newLine;
575     res += QLatin1String("  package:");
576     res += package;
577     res += newLine;
578     res += QLatin1String("  type:");
579     res += type;
580     res += newLine;
581     res += QLatin1String("  version:");
582     res += version.toString();
583     res += newLine;
584     res += QLatin1String("  metaObjectRevision:");
585     res += QString::number(metaObjectRevision);
586     res += newLine;
587     res += QLatin1String("  isValid:");
588     res += QString::number(isValid());
589     res += newLine;
590     res += QLatin1Char('}');
591     return res;
592 }
593 
toString() const594 QString FakeMetaObject::Export::toString() const
595 {
596     return describe();
597 }
598