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 for Python.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "generator.h"
30 #include "ctypenames.h"
31 #include "abstractmetalang.h"
32 #include "parser/codemodel.h"
33 #include "messages.h"
34 #include "reporthandler.h"
35 #include "fileout.h"
36 #include "apiextractor.h"
37 #include "typesystem.h"
38 
39 #include <QtCore/QDir>
40 #include <QtCore/QFile>
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QRegularExpression>
43 #include <QDebug>
44 #include <typedatabase.h>
45 
46 /**
47  * DefaultValue is used for storing default values of types for which code is
48  * generated in different contexts:
49  *
50  * Context             | Example: "Class *"            | Example: "Class" with default Constructor
51  * --------------------+-------------------------------+------------------------------------------
52  * Variable            |  var{nullptr};                | var;
53  * initializations     |                               |
54  * --------------------+-------------------------------+------------------------------------------
55  * Return values       | return nullptr;               | return {}
56  * --------------------+-------------------------------+------------------------------------------
57  * constructor         | static_cast<Class *>(nullptr) | Class()
58  * arguments lists     |                               |
59  * (recursive, precise |                               |
60  * matching).          |                               |
61  */
62 
DefaultValue(Type t,QString value)63 DefaultValue::DefaultValue(Type t, QString value) :
64     m_type(t), m_value(std::move(value))
65 {
66 }
67 
DefaultValue(QString customValue)68 DefaultValue::DefaultValue(QString customValue) :
69     m_type(Custom), m_value(std::move(customValue))
70 {
71 }
72 
returnValue() const73 QString DefaultValue::returnValue() const
74 {
75     switch (m_type) {
76     case DefaultValue::Error:
77         return QLatin1String("#error");
78     case DefaultValue::Boolean:
79         return QLatin1String("false");
80     case DefaultValue::CppScalar:
81         return QLatin1String("0");
82     case DefaultValue::Custom:
83     case DefaultValue::Enum:
84         return m_value;
85     case DefaultValue::Pointer:
86         return QLatin1String("nullptr");
87     case DefaultValue::Void:
88         return QString();
89     case DefaultValue::DefaultConstructorWithDefaultValues:
90         return m_value + QLatin1String("()");
91     case DefaultValue::DefaultConstructor:
92         break;
93     }
94     return QLatin1String("{}");
95 }
96 
initialization() const97 QString DefaultValue::initialization() const
98 {
99     switch (m_type) {
100     case DefaultValue::Error:
101         return QLatin1String("#error");
102     case DefaultValue::Boolean:
103         return QLatin1String("{false}");
104     case DefaultValue::CppScalar:
105         return QLatin1String("{0}");
106     case DefaultValue::Custom:
107         return QLatin1String(" = ") + m_value;
108     case DefaultValue::Enum:
109         return QLatin1Char('{') + m_value + QLatin1Char('}');
110     case DefaultValue::Pointer:
111         return QLatin1String("{nullptr}");
112     case DefaultValue::Void:
113         Q_ASSERT(false);
114         break;
115     case DefaultValue::DefaultConstructor:
116     case DefaultValue::DefaultConstructorWithDefaultValues:
117         break;
118     }
119     return QString();
120 }
121 
constructorParameter() const122 QString DefaultValue::constructorParameter() const
123 {
124     switch (m_type) {
125     case DefaultValue::Error:
126         return QLatin1String("#error");
127     case DefaultValue::Boolean:
128         return QLatin1String("false");
129     case DefaultValue::CppScalar: {
130         // PYSIDE-846: Use static_cast in case of "unsigned long" and similar
131         const QString cast = m_value.contains(QLatin1Char(' '))
132             ? QLatin1String("static_cast<") + m_value + QLatin1Char('>')
133             : m_value;
134         return cast + QLatin1String("(0)");
135     }
136     case DefaultValue::Custom:
137     case DefaultValue::Enum:
138         return m_value;
139     case DefaultValue::Pointer:
140         // Be precise here to be able to differentiate between constructors
141         // taking different pointer types, cf
142         // QTreeWidgetItemIterator(QTreeWidget *) and
143         // QTreeWidgetItemIterator(QTreeWidgetItemIterator *).
144         return QLatin1String("static_cast<") + m_value + QLatin1String("*>(nullptr)");
145     case DefaultValue::Void:
146         Q_ASSERT(false);
147         break;
148     case DefaultValue::DefaultConstructor:
149     case DefaultValue::DefaultConstructorWithDefaultValues:
150         break;
151     }
152     return m_value + QLatin1String("()");
153 }
154 
smartPointerWrapperName() const155 QString GeneratorContext::smartPointerWrapperName() const
156 {
157     Q_ASSERT(m_type == SmartPointer);
158     return m_preciseClassType->cppSignature();
159 }
160 
161 struct Generator::GeneratorPrivate
162 {
163     const ApiExtractor *apiextractor = nullptr;
164     QString outDir;
165     // License comment
166     QString licenseComment;
167     QString moduleName;
168     QStringList instantiatedContainersNames;
169     QVector<const AbstractMetaType *> instantiatedContainers;
170     QVector<const AbstractMetaType *> instantiatedSmartPointers;
171     AbstractMetaClassList m_invisibleTopNamespaces;
172 };
173 
Generator()174 Generator::Generator() : m_d(new GeneratorPrivate)
175 {
176 }
177 
~Generator()178 Generator::~Generator()
179 {
180     delete m_d;
181 }
182 
setup(const ApiExtractor & extractor)183 bool Generator::setup(const ApiExtractor &extractor)
184 {
185     m_d->apiextractor = &extractor;
186     const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType();
187     if (!moduleEntry || !moduleEntry->generateCode()) {
188         qCWarning(lcShiboken) << "Couldn't find the package name!!";
189         return false;
190     }
191 
192     collectInstantiatedContainersAndSmartPointers();
193 
194     for (auto c : classes()) {
195         if (c->enclosingClass() == nullptr && c->isInvisibleNamespace()) {
196             m_d->m_invisibleTopNamespaces.append(c);
197             c->invisibleNamespaceRecursion([&](AbstractMetaClass *ic) {
198                 m_d->m_invisibleTopNamespaces.append(ic);
199             });
200         }
201     }
202 
203     return doSetup();
204 }
205 
getSimplifiedContainerTypeName(const AbstractMetaType * type)206 QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType *type)
207 {
208     const QString signature = type->cppSignature();
209     if (!type->typeEntry()->isContainer() && !type->typeEntry()->isSmartPointer())
210         return signature;
211     QString typeName = signature;
212     if (type->isConstant())
213         typeName.remove(0, sizeof("const ") / sizeof(char) - 1);
214     switch (type->referenceType()) {
215     case NoReference:
216         break;
217     case LValueReference:
218         typeName.chop(1);
219         break;
220     case RValueReference:
221         typeName.chop(2);
222         break;
223     }
224     while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' ')))
225         typeName.chop(1);
226     return typeName;
227 }
228 
229 // Strip a "const QSharedPtr<const Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016/454)
canonicalSmartPtrInstantiation(const AbstractMetaType * type)230 const AbstractMetaType *canonicalSmartPtrInstantiation(const AbstractMetaType *type)
231 {
232     const AbstractMetaTypeList &instantiations = type->instantiations();
233     Q_ASSERT(instantiations.size() == 1);
234     const bool needsFix = type->isConstant() || type->referenceType() != NoReference;
235     const bool pointeeNeedsFix = instantiations.constFirst()->isConstant();
236     if (!needsFix && !pointeeNeedsFix)
237         return type;
238     auto fixedType = type->copy();
239     fixedType->setReferenceType(NoReference);
240     fixedType->setConstant(false);
241     if (pointeeNeedsFix) {
242         auto fixedPointeeType = instantiations.constFirst()->copy();
243         fixedPointeeType->setConstant(false);
244         fixedType->setInstantiations(AbstractMetaTypeList(1, fixedPointeeType));
245     }
246     return fixedType;
247 }
248 
pointeeTypeEntry(const AbstractMetaType * smartPtrType)249 static inline const TypeEntry *pointeeTypeEntry(const AbstractMetaType *smartPtrType)
250 {
251     return smartPtrType->instantiations().constFirst()->typeEntry();
252 }
253 
addInstantiatedContainersAndSmartPointers(const AbstractMetaType * type,const QString & context)254 void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType *type,
255                                                           const QString &context)
256 {
257     if (!type)
258         return;
259     for (const auto *t : type->instantiations())
260         addInstantiatedContainersAndSmartPointers(t, context);
261     const auto typeEntry = type->typeEntry();
262     const bool isContainer = typeEntry->isContainer();
263     if (!isContainer
264         && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) {
265         return;
266     }
267     if (type->hasTemplateChildren()) {
268         QString piece = isContainer ? QStringLiteral("container") : QStringLiteral("smart pointer");
269         QString warning =
270                 QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template"
271                                " arguments.").arg(piece, type->originalTypeDescription());
272         if (!context.isEmpty())
273             warning.append(QStringLiteral(" Calling context: %1").arg(context));
274 
275         qCWarning(lcShiboken).noquote().nospace() << warning;
276         return;
277 
278     }
279     QString typeName = getSimplifiedContainerTypeName(type);
280     if (isContainer) {
281         if (!m_d->instantiatedContainersNames.contains(typeName)) {
282             m_d->instantiatedContainersNames.append(typeName);
283             m_d->instantiatedContainers.append(type);
284         }
285     } else {
286         // Is smart pointer. Check if the (const?) pointee is already known
287         auto pt = pointeeTypeEntry(type);
288         const bool present =
289             std::any_of(m_d->instantiatedSmartPointers.cbegin(), m_d->instantiatedSmartPointers.cend(),
290                         [pt] (const AbstractMetaType *t) {
291                             return pointeeTypeEntry(t) == pt;
292                         });
293         if (!present)
294             m_d->instantiatedSmartPointers.append(canonicalSmartPtrInstantiation(type));
295     }
296 
297 }
298 
collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunction * func)299 void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunction *func)
300 {
301     addInstantiatedContainersAndSmartPointers(func->type(), func->signature());
302     const AbstractMetaArgumentList &arguments = func->arguments();
303     for (const AbstractMetaArgument *arg : arguments)
304         addInstantiatedContainersAndSmartPointers(arg->type(), func->signature());
305 }
306 
collectInstantiatedContainersAndSmartPointers(const AbstractMetaClass * metaClass)307 void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaClass *metaClass)
308 {
309     if (!metaClass->typeEntry()->generateCode())
310         return;
311     const AbstractMetaFunctionList &funcs = metaClass->functions();
312     for (const AbstractMetaFunction *func : funcs)
313         collectInstantiatedContainersAndSmartPointers(func);
314     const AbstractMetaFieldList &fields = metaClass->fields();
315     for (const AbstractMetaField *field : fields)
316         addInstantiatedContainersAndSmartPointers(field->type(), field->name());
317     const AbstractMetaClassList &innerClasses = metaClass->innerClasses();
318     for (AbstractMetaClass *innerClass : innerClasses)
319         collectInstantiatedContainersAndSmartPointers(innerClass);
320 }
321 
collectInstantiatedContainersAndSmartPointers()322 void Generator::collectInstantiatedContainersAndSmartPointers()
323 {
324     for (const AbstractMetaFunction *func : globalFunctions())
325         collectInstantiatedContainersAndSmartPointers(func);
326     for (const AbstractMetaClass *metaClass : classes())
327         collectInstantiatedContainersAndSmartPointers(metaClass);
328 }
329 
instantiatedContainers() const330 QVector<const AbstractMetaType *> Generator::instantiatedContainers() const
331 {
332     return m_d->instantiatedContainers;
333 }
334 
instantiatedSmartPointers() const335 QVector<const AbstractMetaType *> Generator::instantiatedSmartPointers() const
336 {
337     return m_d->instantiatedSmartPointers;
338 }
339 
options() const340 Generator::OptionDescriptions Generator::options() const
341 {
342     return OptionDescriptions();
343 }
344 
handleOption(const QString &,const QString &)345 bool Generator::handleOption(const QString & /* key */, const QString & /* value */)
346 {
347     return false;
348 }
349 
classes() const350 const AbstractMetaClassList &Generator::classes() const
351 {
352     return m_d->apiextractor->classes();
353 }
354 
invisibleTopNamespaces() const355 const AbstractMetaClassList &Generator::invisibleTopNamespaces() const
356 {
357     return m_d->m_invisibleTopNamespaces;
358 }
359 
classesTopologicalSorted(const Dependencies & additionalDependencies) const360 AbstractMetaClassList Generator::classesTopologicalSorted(const Dependencies &additionalDependencies) const
361 {
362     return m_d->apiextractor->classesTopologicalSorted(additionalDependencies);
363 }
364 
globalFunctions() const365 const AbstractMetaFunctionList &Generator::globalFunctions() const
366 {
367     return m_d->apiextractor->globalFunctions();
368 }
369 
globalEnums() const370 const AbstractMetaEnumList &Generator::globalEnums() const
371 {
372     return m_d->apiextractor->globalEnums();
373 }
374 
primitiveTypes() const375 PrimitiveTypeEntryList Generator::primitiveTypes() const
376 {
377     return m_d->apiextractor->primitiveTypes();
378 }
379 
containerTypes() const380 ContainerTypeEntryList Generator::containerTypes() const
381 {
382     return m_d->apiextractor->containerTypes();
383 }
384 
findAbstractMetaEnum(const TypeEntry * typeEntry) const385 const AbstractMetaEnum *Generator::findAbstractMetaEnum(const TypeEntry *typeEntry) const
386 {
387     return m_d->apiextractor->findAbstractMetaEnum(typeEntry);
388 }
389 
findAbstractMetaEnum(const AbstractMetaType * metaType) const390 const AbstractMetaEnum *Generator::findAbstractMetaEnum(const AbstractMetaType *metaType) const
391 {
392     return m_d->apiextractor->findAbstractMetaEnum(metaType->typeEntry());
393 }
394 
licenseComment() const395 QString Generator::licenseComment() const
396 {
397     return m_d->licenseComment;
398 }
399 
setLicenseComment(const QString & licenseComment)400 void Generator::setLicenseComment(const QString &licenseComment)
401 {
402     m_d->licenseComment = licenseComment;
403 }
404 
packageName() const405 QString Generator::packageName() const
406 {
407     return TypeDatabase::instance()->defaultPackageName();
408 }
409 
moduleName() const410 QString Generator::moduleName() const
411 {
412     if (m_d->moduleName.isEmpty()) {
413         m_d->moduleName = packageName();
414         m_d->moduleName.remove(0, m_d->moduleName.lastIndexOf(QLatin1Char('.')) + 1);
415     }
416     return m_d->moduleName;
417 }
418 
outputDirectory() const419 QString Generator::outputDirectory() const
420 {
421     return m_d->outDir;
422 }
423 
setOutputDirectory(const QString & outDir)424 void Generator::setOutputDirectory(const QString &outDir)
425 {
426     m_d->outDir = outDir;
427 }
428 
generateFileForContext(const GeneratorContext & context)429 bool Generator::generateFileForContext(const GeneratorContext &context)
430 {
431     const AbstractMetaClass *cls = context.metaClass();
432 
433     if (!shouldGenerate(cls))
434         return true;
435 
436     const QString fileName = fileNameForContext(context);
437     if (fileName.isEmpty())
438         return true;
439 
440     QString filePath = outputDirectory() + QLatin1Char('/') + subDirectoryForClass(cls)
441             + QLatin1Char('/') + fileName;
442     FileOut fileOut(filePath);
443 
444     generateClass(fileOut.stream, context);
445 
446     return fileOut.done() != FileOut::Failure;
447 }
448 
getFileNameBaseForSmartPointer(const AbstractMetaType * smartPointerType,const AbstractMetaClass * smartPointerClass) const449 QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType,
450                                                   const AbstractMetaClass *smartPointerClass) const
451 {
452     const AbstractMetaType *innerType = smartPointerType->getSmartPointerInnerType();
453     QString fileName = smartPointerClass->qualifiedCppName().toLower();
454     fileName.replace(QLatin1String("::"), QLatin1String("_"));
455     fileName.append(QLatin1String("_"));
456     fileName.append(innerType->name().toLower());
457 
458     return fileName;
459 }
460 
contextForClass(const AbstractMetaClass * c) const461 GeneratorContext Generator::contextForClass(const AbstractMetaClass *c) const
462 {
463     GeneratorContext result;
464     result.m_metaClass = c;
465     return result;
466 }
467 
contextForSmartPointer(const AbstractMetaClass * c,const AbstractMetaType * t) const468 GeneratorContext Generator::contextForSmartPointer(const AbstractMetaClass *c,
469                                                    const AbstractMetaType *t) const
470 {
471     GeneratorContext result;
472     result.m_metaClass = c;
473     result.m_preciseClassType = t;
474     result.m_type = GeneratorContext::SmartPointer;
475     return result;
476 }
477 
generate()478 bool Generator::generate()
479 {
480     const AbstractMetaClassList &classList = m_d->apiextractor->classes();
481     for (AbstractMetaClass *cls : classList) {
482         if (!generateFileForContext(contextForClass(cls)))
483             return false;
484     }
485 
486     const auto smartPointers = m_d->apiextractor->smartPointers();
487     for (const AbstractMetaType *type : qAsConst(m_d->instantiatedSmartPointers)) {
488         AbstractMetaClass *smartPointerClass =
489             AbstractMetaClass::findClass(smartPointers, type->typeEntry());
490         if (!smartPointerClass) {
491             qCWarning(lcShiboken, "%s",
492                       qPrintable(msgCannotFindSmartPointer(type->cppSignature(),
493                                                            smartPointers)));
494             return false;
495         }
496         if (!generateFileForContext(contextForSmartPointer(smartPointerClass, type)))
497             return false;
498     }
499     return finishGeneration();
500 }
501 
shouldGenerateTypeEntry(const TypeEntry * type) const502 bool Generator::shouldGenerateTypeEntry(const TypeEntry *type) const
503 {
504     return type->generateCode() && NamespaceTypeEntry::isVisibleScope(type);
505 }
506 
shouldGenerate(const AbstractMetaClass * metaClass) const507 bool Generator::shouldGenerate(const AbstractMetaClass *metaClass) const
508 {
509     return shouldGenerateTypeEntry(metaClass->typeEntry());
510 }
511 
verifyDirectoryFor(const QString & file)512 void verifyDirectoryFor(const QString &file)
513 {
514     QDir dir = QFileInfo(file).absoluteDir();
515     if (!dir.exists()) {
516         if (!dir.mkpath(dir.absolutePath())) {
517             qCWarning(lcShiboken).noquote().nospace()
518                 << QStringLiteral("unable to create directory '%1'").arg(dir.absolutePath());
519         }
520     }
521 }
522 
replaceTemplateVariables(QString & code,const AbstractMetaFunction * func)523 void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFunction *func)
524 {
525     const AbstractMetaClass *cpp_class = func->ownerClass();
526     if (cpp_class)
527         code.replace(QLatin1String("%TYPE"), cpp_class->name());
528 
529     const AbstractMetaArgumentList &argument = func->arguments();
530     for (AbstractMetaArgument *arg : argument)
531         code.replace(QLatin1Char('%') + QString::number(arg->argumentIndex() + 1), arg->name());
532 
533     //template values
534     code.replace(QLatin1String("%RETURN_TYPE"), translateType(func->type(), cpp_class));
535     code.replace(QLatin1String("%FUNCTION_NAME"), func->originalName());
536 
537     if (code.contains(QLatin1String("%ARGUMENT_NAMES"))) {
538         QString str;
539         QTextStream aux_stream(&str);
540         writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments);
541         code.replace(QLatin1String("%ARGUMENT_NAMES"), str);
542     }
543 
544     if (code.contains(QLatin1String("%ARGUMENTS"))) {
545         QString str;
546         QTextStream aux_stream(&str);
547         writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments);
548         code.replace(QLatin1String("%ARGUMENTS"), str);
549     }
550 }
551 
formatCode(QTextStream & s,const QString & code,Indentor & indentor)552 QTextStream &formatCode(QTextStream &s, const QString &code, Indentor &indentor)
553 {
554     const auto lines= code.splitRef(QLatin1Char('\n'));
555     for (const auto &line : lines) {
556         // Do not indent preprocessor lines
557         if (!line.isEmpty() && !line.startsWith(QLatin1Char('#')))
558             s << indentor;
559         s << line << '\n';
560     }
561     return s;
562 }
563 
implicitConversions(const TypeEntry * type) const564 AbstractMetaFunctionList Generator::implicitConversions(const TypeEntry *type) const
565 {
566     if (type->isValue()) {
567         if (const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), type))
568             return metaClass->implicitConversions();
569     }
570     return AbstractMetaFunctionList();
571 }
572 
implicitConversions(const AbstractMetaType * metaType) const573 AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType *metaType) const
574 {
575     return implicitConversions(metaType->typeEntry());
576 }
577 
isObjectType(const TypeEntry * type)578 bool Generator::isObjectType(const TypeEntry *type)
579 {
580     if (type->isComplex())
581         return Generator::isObjectType(static_cast<const ComplexTypeEntry *>(type));
582     return type->isObject();
583 }
isObjectType(const ComplexTypeEntry * type)584 bool Generator::isObjectType(const ComplexTypeEntry *type)
585 {
586     return type->isObject();
587 }
isObjectType(const AbstractMetaClass * metaClass)588 bool Generator::isObjectType(const AbstractMetaClass *metaClass)
589 {
590     return Generator::isObjectType(metaClass->typeEntry());
591 }
isObjectType(const AbstractMetaType * metaType)592 bool Generator::isObjectType(const AbstractMetaType *metaType)
593 {
594     return isObjectType(metaType->typeEntry());
595 }
596 
isPointer(const AbstractMetaType * type)597 bool Generator::isPointer(const AbstractMetaType *type)
598 {
599     return type->indirections() > 0
600             || type->isNativePointer()
601             || type->isValuePointer();
602 }
603 
isCString(const AbstractMetaType * type)604 bool Generator::isCString(const AbstractMetaType *type)
605 {
606     return type->isNativePointer()
607             && type->indirections() == 1
608             && type->name() == QLatin1String("char");
609 }
610 
isVoidPointer(const AbstractMetaType * type)611 bool Generator::isVoidPointer(const AbstractMetaType *type)
612 {
613     return type->isNativePointer()
614             && type->indirections() == 1
615             && type->name() == QLatin1String("void");
616 }
617 
getFullTypeName(const TypeEntry * type) const618 QString Generator::getFullTypeName(const TypeEntry *type) const
619 {
620     QString result = type->qualifiedCppName();
621     if (type->isArray())
622         type = static_cast<const ArrayTypeEntry *>(type)->nestedTypeEntry();
623     if (!type->isCppPrimitive())
624         result.prepend(QLatin1String("::"));
625     return result;
626 }
627 
getFullTypeName(const AbstractMetaType * type) const628 QString Generator::getFullTypeName(const AbstractMetaType *type) const
629 {
630     if (isCString(type))
631         return QLatin1String("const char*");
632     if (isVoidPointer(type))
633         return QLatin1String("void*");
634     if (type->typeEntry()->isContainer())
635         return QLatin1String("::") + type->cppSignature();
636     QString typeName;
637     if (type->typeEntry()->isComplex() && type->hasInstantiations())
638         typeName = getFullTypeNameWithoutModifiers(type);
639     else
640         typeName = getFullTypeName(type->typeEntry());
641     return typeName + QString::fromLatin1("*").repeated(type->indirections());
642 }
643 
getFullTypeName(const AbstractMetaClass * metaClass) const644 QString Generator::getFullTypeName(const AbstractMetaClass *metaClass) const
645 {
646     return QLatin1String("::") + metaClass->qualifiedCppName();
647 }
648 
getFullTypeNameWithoutModifiers(const AbstractMetaType * type) const649 QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType *type) const
650 {
651     if (isCString(type))
652         return QLatin1String("const char*");
653     if (isVoidPointer(type))
654         return QLatin1String("void*");
655     if (!type->hasInstantiations())
656         return getFullTypeName(type->typeEntry());
657     QString typeName = type->cppSignature();
658     if (type->isConstant())
659         typeName.remove(0, sizeof("const ") / sizeof(char) - 1);
660     switch (type->referenceType()) {
661     case NoReference:
662         break;
663     case LValueReference:
664         typeName.chop(1);
665         break;
666     case RValueReference:
667         typeName.chop(2);
668         break;
669     }
670     while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' ')))
671         typeName.chop(1);
672     return QLatin1String("::") + typeName;
673 }
674 
minimalConstructor(const AbstractMetaType * type) const675 DefaultValue Generator::minimalConstructor(const AbstractMetaType *type) const
676 {
677     if (!type || (type->referenceType() ==  LValueReference && Generator::isObjectType(type)))
678         return DefaultValue(DefaultValue::Error);
679 
680     if (type->isContainer()) {
681         QString ctor = type->cppSignature();
682         if (ctor.endsWith(QLatin1Char('*'))) {
683             ctor.chop(1);
684             return DefaultValue(DefaultValue::Pointer, ctor.trimmed());
685         }
686         if (ctor.startsWith(QLatin1String("const ")))
687             ctor.remove(0, sizeof("const ") / sizeof(char) - 1);
688         if (ctor.endsWith(QLatin1Char('&'))) {
689             ctor.chop(1);
690             ctor = ctor.trimmed();
691         }
692         return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor);
693     }
694 
695     if (type->isNativePointer())
696         return DefaultValue(DefaultValue::Pointer, type->typeEntry()->qualifiedCppName());
697     if (Generator::isPointer(type))
698         return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + type->typeEntry()->qualifiedCppName());
699 
700     if (type->typeEntry()->isSmartPointer())
701         return minimalConstructor(type->typeEntry());
702 
703     if (type->typeEntry()->isComplex()) {
704         auto cType = static_cast<const ComplexTypeEntry *>(type->typeEntry());
705         if (cType->hasDefaultConstructor())
706             return DefaultValue(DefaultValue::Custom, cType->defaultConstructor());
707         auto ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType));
708         if (ctor.isValid() && type->hasInstantiations()) {
709             QString v = ctor.value();
710             v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type));
711             ctor.setValue(v);
712         }
713         return ctor;
714     }
715 
716     return minimalConstructor(type->typeEntry());
717 }
718 
minimalConstructor(const TypeEntry * type) const719 DefaultValue Generator::minimalConstructor(const TypeEntry *type) const
720 {
721     if (!type)
722         return DefaultValue(DefaultValue::Error);
723 
724     if (type->isCppPrimitive()) {
725         const QString &name = type->qualifiedCppName();
726         return name == QLatin1String("bool")
727             ? DefaultValue(DefaultValue::Boolean)
728             : DefaultValue(DefaultValue::CppScalar, name);
729     }
730 
731     if (type->isEnum()) {
732         const auto enumEntry = static_cast<const EnumTypeEntry *>(type);
733         if (const auto *nullValue = enumEntry->nullValue())
734             return DefaultValue(DefaultValue::Enum, nullValue->name());
735         return DefaultValue(DefaultValue::Custom,
736                             QLatin1String("static_cast< ::") + type->qualifiedCppName()
737                             + QLatin1String(">(0)"));
738     }
739 
740     if (type->isFlags()) {
741         return DefaultValue(DefaultValue::Custom,
742                             type->qualifiedCppName() + QLatin1String("(0)"));
743     }
744 
745     if (type->isPrimitive()) {
746         QString ctor = static_cast<const PrimitiveTypeEntry *>(type)->defaultConstructor();
747         // If a non-C++ (i.e. defined by the user) primitive type does not have
748         // a default constructor defined by the user, the empty constructor is
749         // heuristically returned. If this is wrong the build of the generated
750         // bindings will tell.
751         return ctor.isEmpty()
752             ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::")
753                            + type->qualifiedCppName())
754             : DefaultValue(DefaultValue::Custom, ctor);
755     }
756 
757     if (type->isSmartPointer())
758         return DefaultValue(DefaultValue::DefaultConstructor, type->qualifiedCppName());
759 
760     if (type->isComplex())
761         return minimalConstructor(AbstractMetaClass::findClass(classes(), type));
762 
763     return DefaultValue(DefaultValue::Error);
764 }
765 
constructorCall(const QString & qualifiedCppName,const QStringList & args)766 static QString constructorCall(const QString &qualifiedCppName, const QStringList &args)
767 {
768     return QLatin1String("::") + qualifiedCppName + QLatin1Char('(')
769         + args.join(QLatin1String(", ")) + QLatin1Char(')');
770 }
771 
minimalConstructor(const AbstractMetaClass * metaClass) const772 DefaultValue Generator::minimalConstructor(const AbstractMetaClass *metaClass) const
773 {
774     if (!metaClass)
775         return DefaultValue(DefaultValue::Error);
776 
777     auto cType = static_cast<const ComplexTypeEntry *>(metaClass->typeEntry());
778     if (cType->hasDefaultConstructor())
779         return DefaultValue(DefaultValue::Custom, cType->defaultConstructor());
780 
781     const QString qualifiedCppName = cType->qualifiedCppName();
782     // Obtain a list of constructors sorted by complexity and number of arguments
783     QMultiMap<int, const AbstractMetaFunction *> candidates;
784     const AbstractMetaFunctionList &constructors = metaClass->queryFunctions(AbstractMetaClass::Constructors);
785     for (const AbstractMetaFunction *ctor : constructors) {
786         if (!ctor->isUserAdded() && !ctor->isPrivate()
787             && ctor->functionType() == AbstractMetaFunction::ConstructorFunction) {
788             // No arguments: Default constructible
789             const auto &arguments = ctor->arguments();
790             if (arguments.isEmpty()) {
791                 return DefaultValue(DefaultValue::DefaultConstructor,
792                                     QLatin1String("::") + qualifiedCppName);
793             }
794             // First argument has unmodified default: Default constructible with values
795             if (arguments.constFirst()->hasUnmodifiedDefaultValueExpression()) {
796                 return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues,
797                                     QLatin1String("::") + qualifiedCppName);
798             }
799             // Examine arguments, exclude functions taking a self parameter
800             bool simple = true;
801             bool suitable = true;
802             for (int i = 0, size = arguments.size();
803                  suitable && i < size && !arguments.at(i)->hasOriginalDefaultValueExpression(); ++i) {
804                 const AbstractMetaArgument *arg = arguments.at(i);
805                 const TypeEntry *aType = arg->type()->typeEntry();
806                 suitable &= aType != cType;
807                 simple &= aType->isCppPrimitive() || aType->isEnum() || isPointer(arg->type());
808             }
809             if (suitable)
810                 candidates.insert(arguments.size() + (simple ? 0 : 100), ctor);
811         }
812     }
813 
814     for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) {
815         const AbstractMetaArgumentList &arguments = it.value()->arguments();
816         QStringList args;
817         bool ok = true;
818         for (int i =0, size = arguments.size(); ok && i < size; ++i) {
819             const AbstractMetaArgument *arg = arguments.at(i);
820             if (arg->hasModifiedDefaultValueExpression()) {
821                 args << arg->defaultValueExpression(); // Spell out modified values
822                 break;
823             }
824             if (arg->hasOriginalDefaultValueExpression())
825                 break;
826             auto argValue = minimalConstructor(arg->type());
827             ok &= argValue.isValid();
828             args << argValue.constructorParameter();
829         }
830         if (ok)
831             return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args));
832     }
833 
834     return DefaultValue(DefaultValue::Error);
835 }
836 
837 // Should int be used for a (protected) enum when generating the public wrapper?
useEnumAsIntForProtectedHack(const AbstractMetaType * metaType) const838 bool Generator::useEnumAsIntForProtectedHack(const AbstractMetaType *metaType) const
839 {
840     if (metaType->isFlags())
841         return true;
842     if (!metaType->isEnum())
843         return false;
844     const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(metaType);
845     if (!metaEnum)
846         return true;
847     if (metaEnum->attributes() & AbstractMetaAttributes::Public) // No reason, type is public
848         return false;
849     // Only ordinary C-enums can be used as int, scoped enums fail when used
850     // as function arguments.
851     if (metaEnum->enumKind() == EnumKind::EnumClass)
852         qWarning(lcShiboken, "%s", qPrintable(msgCannotUseEnumAsInt(metaEnum->name())));
853     return true;
854 }
855 
translateType(const AbstractMetaType * cType,const AbstractMetaClass * context,Options options) const856 QString Generator::translateType(const AbstractMetaType *cType,
857                                  const AbstractMetaClass *context,
858                                  Options options) const
859 {
860     QString s;
861     static int constLen = strlen("const");
862 
863     if (context && cType &&
864         context->typeEntry()->isGenericClass() &&
865         cType->originalTemplateType()) {
866         cType = cType->originalTemplateType();
867     }
868 
869     if (!cType) {
870         s = QLatin1String("void");
871     } else if (cType->isArray()) {
872         s = translateType(cType->arrayElementType(), context, options) + QLatin1String("[]");
873     } else if ((options & Generator::EnumAsInts) && useEnumAsIntForProtectedHack(cType)) {
874         s = intT();
875     } else {
876         if (options & Generator::OriginalName) {
877             s = cType->originalTypeDescription().trimmed();
878             if ((options & Generator::ExcludeReference) && s.endsWith(QLatin1Char('&')))
879                 s.chop(1);
880 
881             // remove only the last const (avoid remove template const)
882             if (options & Generator::ExcludeConst) {
883                 int index = s.lastIndexOf(QLatin1String("const"));
884 
885                 if (index >= (s.size() - (constLen + 1))) // (VarType const)  or (VarType const[*|&])
886                     s = s.remove(index, constLen);
887             }
888         } else if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) {
889             AbstractMetaType *copyType = cType->copy();
890 
891             if (options & Generator::ExcludeConst)
892                 copyType->setConstant(false);
893 
894             if (options & Generator::ExcludeReference)
895                 copyType->setReferenceType(NoReference);
896 
897             s = copyType->cppSignature();
898             if (!copyType->typeEntry()->isVoid() && !copyType->typeEntry()->isCppPrimitive())
899                 s.prepend(QLatin1String("::"));
900             delete copyType;
901         } else {
902             s = cType->cppSignature();
903         }
904     }
905 
906     return s;
907 }
908 
909 
subDirectoryForClass(const AbstractMetaClass * clazz) const910 QString Generator::subDirectoryForClass(const AbstractMetaClass *clazz) const
911 {
912     return subDirectoryForPackage(clazz->package());
913 }
914 
subDirectoryForPackage(QString packageNameIn) const915 QString Generator::subDirectoryForPackage(QString packageNameIn) const
916 {
917     if (packageNameIn.isEmpty())
918         packageNameIn = packageName();
919     packageNameIn.replace(QLatin1Char('.'), QDir::separator());
920     return packageNameIn;
921 }
922 
923 template<typename T>
getClassTargetFullName_(const T * t,bool includePackageName)924 static QString getClassTargetFullName_(const T *t, bool includePackageName)
925 {
926     QString name = t->name();
927     const AbstractMetaClass *context = t->enclosingClass();
928     while (context) {
929         // If the type was marked as 'visible=false' we should not use it in
930         // the type name
931         if (NamespaceTypeEntry::isVisibleScope(context->typeEntry())) {
932             name.prepend(QLatin1Char('.'));
933             name.prepend(context->name());
934         }
935         context = context->enclosingClass();
936     }
937     if (includePackageName) {
938         name.prepend(QLatin1Char('.'));
939         name.prepend(t->package());
940     }
941     return name;
942 }
943 
getClassTargetFullName(const AbstractMetaClass * metaClass,bool includePackageName)944 QString getClassTargetFullName(const AbstractMetaClass *metaClass, bool includePackageName)
945 {
946     return getClassTargetFullName_(metaClass, includePackageName);
947 }
948 
getClassTargetFullName(const AbstractMetaEnum * metaEnum,bool includePackageName)949 QString getClassTargetFullName(const AbstractMetaEnum *metaEnum, bool includePackageName)
950 {
951     return getClassTargetFullName_(metaEnum, includePackageName);
952 }
953 
getClassTargetFullName(const AbstractMetaType * metaType,bool includePackageName)954 QString getClassTargetFullName(const AbstractMetaType *metaType, bool includePackageName)
955 {
956     QString name = metaType->cppSignature();
957     name.replace(QLatin1String("::"), QLatin1String("_"));
958     name.replace(QLatin1Char('<'), QLatin1Char('_'));
959     name.remove(QLatin1Char('>'));
960     name.remove(QLatin1Char(' '));
961     if (includePackageName) {
962         name.prepend(QLatin1Char('.'));
963         name.prepend(metaType->package());
964     }
965     return name;
966 }
967 
getFilteredCppSignatureString(QString signature)968 QString getFilteredCppSignatureString(QString signature)
969 {
970     signature.replace(QLatin1String("::"), QLatin1String("_"));
971     signature.replace(QLatin1Char('<'), QLatin1Char('_'));
972     signature.replace(QLatin1Char('>'), QLatin1Char('_'));
973     signature.replace(QLatin1Char(' '), QLatin1Char('_'));
974     return signature;
975 }
976