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