Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3f20b42
Don't interpret "= default" or "= delete" to mean this is an abstract…
usiems Nov 3, 2023
9a08b85
Deleted methods need to marked as "invalid" to be handled correctly
usiems Nov 6, 2023
6abdb1f
Cover cases where noexcept comes with an expression
usiems Nov 4, 2023
cc312c2
Implement and use Parser::skipFunctionBody
usiems Nov 3, 2023
e16110d
Simplify skipFunctionBody by only looking at curly braces
usiems Nov 9, 2023
4de21d5
Avoid currently unsupported use of "noexcept(...)"
usiems Nov 3, 2023
a9cf7ac
Ignore some more macros that lead to parser errors
usiems Nov 4, 2023
09caf99
Support basic new-style initializers
usiems Nov 4, 2023
66df824
Add missing enum used as default value for a method
usiems Nov 6, 2023
37f98c7
Automatically mark methods with r-value references (&&) as invalid
usiems Nov 6, 2023
f3c0c9a
Don't create wrappers for cbegin and cend
usiems Nov 8, 2023
d9901c5
Hopefully make generator immune against iterator order changes for ty…
usiems Nov 8, 2023
5d345bb
Don't generate wrappers for templated methods in non-template classes
usiems Nov 8, 2023
dd8f906
Fix handling of right-shift as template bracket
mrbean-bremen Nov 1, 2023
d129faf
Ignore variadic template parameters
mrbean-bremen Nov 1, 2023
164969d
Handle ellipsis in type parser
usiems Nov 9, 2023
393af73
Fix: AbstractMetaBuilder::classesTopologicalSorted (#148)
jbowler Nov 10, 2023
73993d4
Improve debugging code for rewind, also show identifiers
usiems Nov 10, 2023
6fdafa1
Add qsizetype and qptrdiff as primitive types
usiems Nov 15, 2023
e804594
Avoid crash if supposed property setter has no arguments
usiems Nov 14, 2023
2aecabb
Avoid crash in lexer by increasing size of token_stream early
usiems Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 133 additions & 60 deletions generator/abstractmetabuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ bool AbstractMetaBuilder::build()
file.close();

Control control;
control.setSkipFunctionBody(true);
Parser p(&control);
pool __pool;

Expand Down Expand Up @@ -616,7 +617,15 @@ void AbstractMetaBuilder::addAbstractMetaClass(AbstractMetaClass *cls)

cls->setOriginalAttributes(cls->attributes());
if (cls->typeEntry()->isContainer()) {
m_templates << cls;
QString name = cls->typeEntry()->name();
if (cls->functions().size() || cls->baseClassNames().size()) {
if (!m_templates.contains(name)) {
m_templates[name] = cls;
}
else {
ReportHandler::warning(QString("Duplicate container type template '%1'").arg(name));
}
}
} else {
m_meta_classes << cls;
if (cls->typeEntry()->designatedInterface()) {
Expand Down Expand Up @@ -1095,6 +1104,7 @@ AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem class_item)
template_args.append(param_type);
}
meta_class->setTemplateArguments(template_args);
meta_class->setHasActualDeclaration(class_item->hasActualDeclaration());

parseQ_Property(meta_class, class_item->propertyDeclarations());

Expand Down Expand Up @@ -1129,8 +1139,9 @@ AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem class_item)

m_current_class = old_current_class;

// Set the default include file name
if (!type->include().isValid()) {
// Set the default include file name. In case we saw an template instance earlier,
// overwrite the include file when we see the actual declaration.
if (!type->include().isValid() || class_item->hasActualDeclaration()) {
QFileInfo info(class_item->fileName());
type->setInclude(Include(Include::IncludePath, info.fileName()));
}
Expand Down Expand Up @@ -1242,7 +1253,7 @@ void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scope_item, AbstractM
}
} else if (QPropertySpec *write =
meta_class->propertySpecForWrite(meta_function->name())) {
if (write->type() == meta_function->arguments().at(0)->type()->typeEntry()) {
if (meta_function->arguments().size() == 1 && write->type() == meta_function->arguments().at(0)->type()->typeEntry()) {
*meta_function += AbstractMetaAttributes::PropertyWriter;
meta_function->setPropertySpec(write);
// printf("%s is writer for %s\n",
Expand Down Expand Up @@ -1336,13 +1347,7 @@ bool AbstractMetaBuilder::setupInheritance(AbstractMetaClass *meta_class)
TypeParser::Info info = TypeParser::parse(complete_name);
QString base_name = info.qualified_name.join("::");

AbstractMetaClass *templ = 0;
for (AbstractMetaClass *c : m_templates) {
if (c->typeEntry()->name() == base_name) {
templ = c;
break;
}
}
AbstractMetaClass *templ = m_templates.value(base_name);

if (templ == 0)
templ = m_meta_classes.findClass(base_name);
Expand Down Expand Up @@ -1486,6 +1491,12 @@ AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem fu
return 0;
}

// Also filter out functions with template parameters in classes without template arguments
// (we don't support templated classes directly, but derived classes might derive from template instantiations)
if (function_item->templateParameters().size() && m_current_class->templateArguments().empty()) {
return 0;
}

if (function_item->isAuto()) {
/*TODO: it might work just to output 'auto', but this would require
* understanding what AbstractMetabuild::translateType() does and
Expand Down Expand Up @@ -1593,6 +1604,11 @@ AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem fu
meta_function->setFunctionType(AbstractMetaFunction::SlotFunction);
}

if (function_item->isDeleted()) {
meta_function->setInvalid(true);
return meta_function;
}

ArgumentList arguments = function_item->arguments();
AbstractMetaArgumentList meta_arguments;

Expand Down Expand Up @@ -1696,7 +1712,7 @@ AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, boo

}

if (typei.isFunctionPointer()) {
if (typei.isFunctionPointer() || typei.isRvalueReference()) { // function pointers or r-value references are not supported
*ok = false;
return 0;
}
Expand Down Expand Up @@ -2485,55 +2501,112 @@ void AbstractMetaBuilder::dumpLog()

AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted() const
{
AbstractMetaClassList res;

AbstractMetaClassList classes = m_meta_classes;
classes.sort();

QSet<AbstractMetaClass*> noDependency;
QHash<AbstractMetaClass*, QSet<AbstractMetaClass* >* > hash;
for (AbstractMetaClass *cls : classes) {
QSet<AbstractMetaClass* > *depends = new QSet<AbstractMetaClass* >();
/* This function is the standard topological sort of a Directed Acyclic
* Graph (a DAG). It outputs a partially ordered list of the nodes in the
* graph such that a node is output after all of its children.
*
* In the previous implementation it seemed to account for around 68-69% of
* the entire run time of pythonqt_generator however this might be an
* artefact of the profiling technique used as these changes make not
* significant difference to the run time of the generator.
*
* However the previous implementation also leaked memory and modified a
* QHash while iterating over it with a QHashIterator; a potentially fatal
* "use after free".
*
* This version is somewhat like the python equivalent using list
* comprehensions.
*/

/* Build a hash list of QSet<class> for each class. 'class' is represented
* by a pointer to the AbstractMetaClass from m_meta_classes.
*/
ReportHandler::debugSparse(QString("TSORT: %1 meta classes")
.arg(m_meta_classes.count()));
QHash<AbstractMetaClass*, QSet<AbstractMetaClass*>> classes;
classes.reserve(m_meta_classes.count());

for (auto cls : m_meta_classes) {
/* Add the baseClass and the interfaces the class uses. The latter
* are stored in an AbstractMetaClassList which is uses a QList, so:
*/
auto entry(classes.insert(cls,
# if QT_VERSION < QT_VERSION_CHECK(5,14,0)
QSet<AbstractMetaClass*>::fromList(cls->interfaces())
# else
QSet<AbstractMetaClass*>(cls->interfaces().cbegin(),
cls->interfaces().cend())
# endif
));

if (cls->baseClass())
depends->insert(cls->baseClass());

for (AbstractMetaClass *interface : cls->interfaces()) {
AbstractMetaClass *impl = interface->primaryInterfaceImplementor();
if (impl == cls)
continue;
depends->insert(impl);
}

if (depends->empty()) {
noDependency.insert(cls);
} else {
hash.insert(cls, depends);
}
}

while (!noDependency.empty()) {
for (AbstractMetaClass *cls : noDependency.values()) {
if(!cls->isInterface())
res.append(cls);
noDependency.remove(cls);
QHashIterator<AbstractMetaClass*, QSet<AbstractMetaClass* >* > i(hash);
while (i.hasNext()) {
i.next();
i.value()->remove(cls);
if (i.value()->empty()) {
AbstractMetaClass *key = i.key();
noDependency.insert(key);
hash.remove(key);
delete(i.value());
}
}
}
}

if (!noDependency.empty() || !hash.empty()) {
qWarning("dependency graph was cyclic.");
}
entry.value().insert(cls->baseClass());
entry.value().remove(cls); // may come from interfaces
}

/* This and the qFatals below are fatal internal errors in the code of
* pythonqt_generator.
*/
if (m_meta_classes.count() != classes.count())
qFatal("TOPO SORT: duplicate meta classes (%lld != %lld)",
static_cast<long long>(m_meta_classes.count()),
static_cast<long long>(classes.count()));

/* Loop: output all the classes with no remaining dependencies to the
* result and then also remove those now output classes from the remaining
* classes in the hash table.
*/
AbstractMetaClassList result;
result.reserve(classes.count());
QSet<AbstractMetaClass *> handled;
handled.reserve(classes.count());
int interfaceClasses(0), depthFromLeaf(0);

while (!classes.empty()) {
handled.clear();
int iFCount(0);

/* Output classes where all children have already been output (initially
* the leaf nodes):
*/
for (auto i(classes.cbegin()); i != classes.cend(); ++i)
if (i.value().empty())
handled.insert(i.key());

return res;
/* Something must have been done; if not this is not a DAG because
* there is a cycle.
*/
if (handled.empty())
qFatal("TOPOSORT: %lld cyclic meta classes @depth %d.",
static_cast<long long>(classes.count()), depthFromLeaf);

/* Remove all 'handled' from the hash table. */
for (auto cls : handled)
if (!classes.remove(cls))
qFatal("TOPO SORT: class remove failed @depth %d.",
depthFromLeaf);

/* Then remove the 'handled' set from the classes values: */
for (QSet<AbstractMetaClass*> &set : classes)
set -= handled;

/* Output only those handled classes there are not interfaces: */
for (auto cls : handled) {
if (!cls->isInterface())
result.append(cls);
else
++iFCount;
}

ReportHandler::debugSparse(
QString("TSORT: depth %1: %2 classes (%3 interface)")
.arg(depthFromLeaf).arg(handled.count()).arg(iFCount));
interfaceClasses += iFCount;
++depthFromLeaf;
}

ReportHandler::debugSparse(QString(
"TSORT: %1 result classes, %2 interface classes)")
.arg(result.count()).arg(interfaceClasses));
return result;
}
2 changes: 1 addition & 1 deletion generator/abstractmetabuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class AbstractMetaBuilder
QString m_file_name;

AbstractMetaClassList m_meta_classes;
AbstractMetaClassList m_templates;
QHash<QString,AbstractMetaClass*> m_templates;
FileModelItem m_dom;

QSet<const TypeEntry *> m_used_types;
Expand Down
7 changes: 6 additions & 1 deletion generator/abstractmetalang.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ class AbstractMetaClass : public AbstractMetaAttributes
m_has_equals_operator(false),
m_has_clone_operator(false),
m_is_type_alias(false),
m_has_actual_declaration(false),
m_qDebug_stream_function(0)
{
}
Expand Down Expand Up @@ -796,6 +797,9 @@ class AbstractMetaClass : public AbstractMetaAttributes
void setHasCloneOperator(bool on) { m_has_clone_operator = on; }
bool hasCloneOperator() const { return m_has_clone_operator; }

void setHasActualDeclaration(bool on) { m_has_actual_declaration = on; }
bool hasActualDeclaration() const { return m_has_actual_declaration; }

QString getDefaultNonZeroFunction() const;

void addPropertySpec(QPropertySpec *spec) { m_property_specs << spec; }
Expand Down Expand Up @@ -856,7 +860,8 @@ class AbstractMetaClass : public AbstractMetaAttributes
uint m_has_equals_operator : 1;
uint m_has_clone_operator :1;
uint m_is_type_alias : 1;
uint m_reserved : 18;
uint m_has_actual_declaration : 1;
uint m_reserved : 17;
QString m_destructor_exception;

const AbstractMetaClass *m_enclosing_class{};
Expand Down
2 changes: 2 additions & 0 deletions generator/parser/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ struct InitializerAST: public AST

InitializerClauseAST *initializer_clause{};
ExpressionAST *expression{};
bool isDefault{};
bool isDeleted{};
};

struct InitializerClauseAST: public AST
Expand Down
5 changes: 4 additions & 1 deletion generator/parser/binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ void Binder::declare_symbol(SimpleDeclarationAST *node, InitDeclaratorAST *init_
fun->setAccessPolicy(_M_current_access);
fun->setFunctionType(_M_current_function_type);
fun->setName(name_cc.name());
fun->setAbstract(init_declarator->initializer != 0);
InitializerAST* initializer = init_declarator->initializer;
fun->setDeleted(initializer && initializer->isDeleted);
fun->setAbstract(initializer && !initializer->isDefault && !initializer->isDeleted); // must be "= 0"
fun->setConstant(declarator->fun_cv != 0);
fun->setException(exceptionSpecToString(declarator->exception_spec));

Expand Down Expand Up @@ -727,6 +729,7 @@ void Binder::visitClassSpecifier(ClassSpecifierAST *node)
name_cc.run(node->name->unqualified_name);
_M_context.append(name_cc.name());
visitNodes(this, node->member_specs);
_M_current_class->setHasActualDeclaration(node->member_specs);
_M_context.removeLast();

changeCurrentClass(old);
Expand Down
10 changes: 10 additions & 0 deletions generator/parser/codemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,16 @@ void _FunctionModelItem::setAbstract(bool isAbstract)
_M.isAbstract = isAbstract;
}

bool _FunctionModelItem::isDeleted() const
{
return _M.isDeleted;
}

void _FunctionModelItem::setDeleted(bool isDeleted)
{
_M.isDeleted = isDeleted;
}

// Qt
bool _FunctionModelItem::isInvokable() const
{
Expand Down
10 changes: 10 additions & 0 deletions generator/parser/codemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ class _ClassModelItem: public _ScopeModelItem
void addPropertyDeclaration(const QString &propertyDeclaration);
QStringList propertyDeclarations() const { return _M_propertyDeclarations; }

void setHasActualDeclaration(bool flag) { _M_hasActualDeclaration = flag; }
bool hasActualDeclaration() const { return _M_hasActualDeclaration; }


protected:
_ClassModelItem(CodeModel *model, int kind = __node_kind)
: _ScopeModelItem(model, kind), _M_classType(CodeModel::Class) {}
Expand All @@ -377,6 +381,8 @@ class _ClassModelItem: public _ScopeModelItem

QStringList _M_propertyDeclarations;

bool _M_hasActualDeclaration{};

private:
_ClassModelItem(const _ClassModelItem &other);
void operator = (const _ClassModelItem &other);
Expand Down Expand Up @@ -566,6 +572,9 @@ class _FunctionModelItem: public _MemberModelItem
bool isAbstract() const;
void setAbstract(bool isAbstract);

bool isDeleted() const;
void setDeleted(bool isDeleted);

bool isVariadics() const;
void setVariadics(bool isVariadics);

Expand All @@ -589,6 +598,7 @@ class _FunctionModelItem: public _MemberModelItem
uint isVirtual: 1;
uint isInline: 1;
uint isAbstract: 1;
uint isDeleted: 1;
uint isExplicit: 1;
uint isVariadics: 1;
uint isInvokable : 1; // Qt
Expand Down
Loading