|
|
/****************************************************************************
|
|
|
**
|
|
|
** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
** All rights reserved.
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
**
|
|
|
** This file is part of the Qt Script Generator project on Qt Labs.
|
|
|
**
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
** No Commercial Usage
|
|
|
** This file contains pre-release code and may not be distributed.
|
|
|
** You may use this file in accordance with the terms and conditions
|
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
|
** this package.
|
|
|
**
|
|
|
** GNU Lesser General Public License Usage
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
** packaging of this file. Please review the following information to
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
**
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
**
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
** Nokia at qt-info@nokia.com.
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
**
|
|
|
** $QT_END_LICENSE$
|
|
|
**
|
|
|
****************************************************************************/
|
|
|
|
|
|
#include "abstractmetabuilder.h"
|
|
|
#include "reporthandler.h"
|
|
|
|
|
|
#include "ast.h"
|
|
|
#include "binder.h"
|
|
|
#include "control.h"
|
|
|
#include "default_visitor.h"
|
|
|
#include "dumptree.h"
|
|
|
#include "lexer.h"
|
|
|
#include "parser.h"
|
|
|
#include "tokens.h"
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
#include <QtCore/QFile>
|
|
|
#include <QtCore/QFileInfo>
|
|
|
#include <QtCore/QTextCodec>
|
|
|
#include <QtCore/QTextStream>
|
|
|
#include <QtCore/QVariant>
|
|
|
|
|
|
static QString strip_template_args(const QString &name)
|
|
|
{
|
|
|
int pos = name.indexOf('<');
|
|
|
return pos < 0 ? name : name.left(pos);
|
|
|
}
|
|
|
|
|
|
static QHash<QString, QString> *operator_names;
|
|
|
QString rename_operator(const QString &oper)
|
|
|
{
|
|
|
QString op = oper.trimmed();
|
|
|
if (!operator_names) {
|
|
|
operator_names = new QHash<QString, QString>;
|
|
|
|
|
|
operator_names->insert("+", "__add__");
|
|
|
operator_names->insert("-", "__sub__");
|
|
|
operator_names->insert("*", "__mul__");
|
|
|
operator_names->insert("/", "__div__");
|
|
|
operator_names->insert("%", "__mod__");
|
|
|
operator_names->insert("&", "__and__");
|
|
|
operator_names->insert("|", "__or__");
|
|
|
operator_names->insert("^", "__xor__");
|
|
|
operator_names->insert("~", "__invert__");
|
|
|
operator_names->insert("<<", "__lshift__");
|
|
|
operator_names->insert(">>", "__rshift__");
|
|
|
|
|
|
// assigments
|
|
|
operator_names->insert("=", "assign");
|
|
|
operator_names->insert("+=", "__iadd__");
|
|
|
operator_names->insert("-=", "__isub__");
|
|
|
operator_names->insert("*=", "__imul__");
|
|
|
operator_names->insert("/=", "__idiv__");
|
|
|
operator_names->insert("%=", "__imod__");
|
|
|
operator_names->insert("&=", "__iand__");
|
|
|
operator_names->insert("|=", "__ior__");
|
|
|
operator_names->insert("^=", "__ixor__");
|
|
|
operator_names->insert("<<=", "__ilshift__");
|
|
|
operator_names->insert(">>=", "__irshift__");
|
|
|
|
|
|
// Logical
|
|
|
operator_names->insert("&&", "logical_and");
|
|
|
operator_names->insert("||", "logical_or");
|
|
|
operator_names->insert("!", "not");
|
|
|
|
|
|
// incr/decr
|
|
|
operator_names->insert("++", "increment");
|
|
|
operator_names->insert("--", "decrement");
|
|
|
|
|
|
// compare
|
|
|
operator_names->insert("<", "__lt__");
|
|
|
operator_names->insert(">", "__gt__");
|
|
|
operator_names->insert("<=", "__le__");
|
|
|
operator_names->insert(">=", "__ge__");
|
|
|
operator_names->insert("!=", "__ne__");
|
|
|
operator_names->insert("==", "__eq__");
|
|
|
|
|
|
// other
|
|
|
operator_names->insert("[]", "subscript");
|
|
|
operator_names->insert("->", "pointer");
|
|
|
}
|
|
|
|
|
|
if (!operator_names->contains(op)) {
|
|
|
TypeDatabase *tb = TypeDatabase::instance();
|
|
|
|
|
|
TypeParser::Info typeInfo = TypeParser::parse(op);
|
|
|
QString cast_to_name = typeInfo.qualified_name.join("::");
|
|
|
TypeEntry *te = tb->findType(cast_to_name);
|
|
|
if ((te && te->codeGeneration() == TypeEntry::GenerateNothing)
|
|
|
|| tb->isClassRejected(cast_to_name)) {
|
|
|
return QString();
|
|
|
} else if (te) {
|
|
|
return "operator_cast_" + typeInfo.qualified_name.join("_");
|
|
|
} else {
|
|
|
ReportHandler::warning(QString("unknown operator '%1'").arg(op));
|
|
|
return "operator " + op;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QString r = operator_names->value(op);
|
|
|
if (r.startsWith("__")) {
|
|
|
return r;
|
|
|
} else {
|
|
|
return "operator_" + r;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AbstractMetaBuilder::AbstractMetaBuilder()
|
|
|
: m_current_class(0)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::checkFunctionModifications()
|
|
|
{
|
|
|
TypeDatabase *types = TypeDatabase::instance();
|
|
|
SingleTypeEntryHash entryHash = types->entries();
|
|
|
QList<TypeEntry *> entries = entryHash.values();
|
|
|
foreach (TypeEntry *entry, entries) {
|
|
|
if (entry == 0)
|
|
|
continue;
|
|
|
if (!entry->isComplex() || entry->codeGeneration() == TypeEntry::GenerateNothing)
|
|
|
continue;
|
|
|
|
|
|
ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(entry);
|
|
|
FunctionModificationList modifications = centry->functionModifications();
|
|
|
|
|
|
foreach (FunctionModification modification, modifications) {
|
|
|
QString signature = modification.signature;
|
|
|
|
|
|
QString name = signature.trimmed();
|
|
|
name = name.mid(0, signature.indexOf("("));
|
|
|
|
|
|
AbstractMetaClass *clazz = m_meta_classes.findClass(centry->qualifiedCppName());
|
|
|
if (clazz == 0)
|
|
|
continue;
|
|
|
|
|
|
AbstractMetaFunctionList functions = clazz->functions();
|
|
|
bool found = false;
|
|
|
QStringList possibleSignatures;
|
|
|
foreach (AbstractMetaFunction *function, functions) {
|
|
|
if (function->minimalSignature() == signature && function->implementingClass() == clazz) {
|
|
|
found = true;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (function->originalName() == name)
|
|
|
possibleSignatures.append(function->minimalSignature() + " in " + function->implementingClass()->name());
|
|
|
}
|
|
|
|
|
|
if (!found) {
|
|
|
QString warning
|
|
|
= QString("signature '%1' for function modification in '%2' not found. Possible candidates: %3")
|
|
|
.arg(signature)
|
|
|
.arg(clazz->qualifiedCppName())
|
|
|
.arg(possibleSignatures.join(", "));
|
|
|
|
|
|
ReportHandler::warning(warning);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AbstractMetaClass *AbstractMetaBuilder::argumentToClass(ArgumentModelItem argument)
|
|
|
{
|
|
|
AbstractMetaClass *returned = 0;
|
|
|
bool ok = false;
|
|
|
AbstractMetaType *type = translateType(argument->type(), &ok);
|
|
|
if (ok && type != 0 && type->typeEntry() != 0 && type->typeEntry()->isComplex()) {
|
|
|
const TypeEntry *entry = type->typeEntry();
|
|
|
returned = m_meta_classes.findClass(entry->name());
|
|
|
}
|
|
|
delete type;
|
|
|
return returned;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Checks the argument of a hash function and flags the type if it is a complex type
|
|
|
*/
|
|
|
void AbstractMetaBuilder::registerHashFunction(FunctionModelItem function_item)
|
|
|
{
|
|
|
ArgumentList arguments = function_item->arguments();
|
|
|
if (arguments.size() == 1) {
|
|
|
if (AbstractMetaClass *cls = argumentToClass(arguments.at(0)))
|
|
|
cls->setHasHashFunction(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Check if a class has a debug stream operator that can be used as toString
|
|
|
*/
|
|
|
|
|
|
void AbstractMetaBuilder::registerToStringCapability(FunctionModelItem function_item)
|
|
|
{
|
|
|
ArgumentList arguments = function_item->arguments();
|
|
|
if (arguments.size() == 2) {
|
|
|
if (arguments.at(0)->type().toString() == "QDebug"){
|
|
|
ArgumentModelItem arg = arguments.at(1);
|
|
|
if (AbstractMetaClass *cls = argumentToClass(arg)) {
|
|
|
if (arg->type().indirections() < 2 && cls->name()!="QObject") {
|
|
|
cls->setToStringCapability(function_item);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseCompareOperator(FunctionModelItem item) {
|
|
|
ArgumentList arguments = item->arguments();
|
|
|
if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
|
|
|
AbstractMetaClass *comparer_class = argumentToClass(arguments.at(0));
|
|
|
AbstractMetaClass *compared_class = argumentToClass(arguments.at(1));
|
|
|
if (comparer_class != 0 && compared_class != 0) {
|
|
|
AbstractMetaClass *old_current_class = m_current_class;
|
|
|
m_current_class = comparer_class;
|
|
|
|
|
|
AbstractMetaFunction *meta_function = traverseFunction(item);
|
|
|
if (meta_function != 0 && !meta_function->isInvalid()) {
|
|
|
// Strip away first argument, since that is the containing object
|
|
|
AbstractMetaArgumentList arguments = meta_function->arguments();
|
|
|
arguments.pop_front();
|
|
|
meta_function->setArguments(arguments);
|
|
|
|
|
|
meta_function->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
|
|
|
|
|
|
meta_function->setOriginalAttributes(meta_function->attributes());
|
|
|
setupFunctionDefaults(meta_function, comparer_class);
|
|
|
|
|
|
comparer_class->addFunction(meta_function);
|
|
|
} else if (meta_function != 0) {
|
|
|
delete meta_function;
|
|
|
}
|
|
|
|
|
|
m_current_class = old_current_class;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseStreamOperator(FunctionModelItem item)
|
|
|
{
|
|
|
ArgumentList arguments = item->arguments();
|
|
|
if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
|
|
|
AbstractMetaClass *streamClass = argumentToClass(arguments.at(0));
|
|
|
AbstractMetaClass *streamedClass = argumentToClass(arguments.at(1));
|
|
|
|
|
|
if (streamClass != 0 && streamedClass != 0
|
|
|
&& (streamClass->name() == "QDataStream" || streamClass->name() == "QTextStream")) {
|
|
|
AbstractMetaClass *old_current_class = m_current_class;
|
|
|
m_current_class = streamedClass;
|
|
|
AbstractMetaFunction *streamFunction = traverseFunction(item);
|
|
|
|
|
|
if (streamFunction != 0 && !streamFunction->isInvalid()) {
|
|
|
QString name = item->name();
|
|
|
streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
|
|
|
|
|
|
if (name.endsWith("<<"))
|
|
|
streamFunction->setName("writeTo");
|
|
|
else
|
|
|
streamFunction->setName("readFrom");
|
|
|
|
|
|
// Strip away last argument, since that is the containing object
|
|
|
AbstractMetaArgumentList arguments = streamFunction->arguments();
|
|
|
arguments.pop_back();
|
|
|
streamFunction->setArguments(arguments);
|
|
|
|
|
|
*streamFunction += AbstractMetaAttributes::Final;
|
|
|
*streamFunction += AbstractMetaAttributes::Public;
|
|
|
streamFunction->setOriginalAttributes(streamFunction->attributes());
|
|
|
|
|
|
streamFunction->setType(0);
|
|
|
|
|
|
setupFunctionDefaults(streamFunction, streamedClass);
|
|
|
|
|
|
streamedClass->addFunction(streamFunction);
|
|
|
streamedClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include());
|
|
|
|
|
|
m_current_class = old_current_class;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseBinaryArithmeticOperator(FunctionModelItem item)
|
|
|
{
|
|
|
ArgumentList arguments = item->arguments();
|
|
|
if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
|
|
|
AbstractMetaClass *aClass = argumentToClass(arguments.at(0));
|
|
|
AbstractMetaClass *bClass = argumentToClass(arguments.at(1));
|
|
|
|
|
|
if (!aClass) return;
|
|
|
|
|
|
AbstractMetaClass *old_current_class = m_current_class;
|
|
|
m_current_class = aClass;
|
|
|
AbstractMetaFunction *streamFunction = traverseFunction(item);
|
|
|
if (streamFunction != 0 && !streamFunction->isInvalid()) {
|
|
|
QString name = rename_operator(item->name().mid(8));
|
|
|
if (name.isEmpty()) return;
|
|
|
|
|
|
streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
|
|
|
streamFunction->setName(name);
|
|
|
|
|
|
// Strip away the first argument, since that is the operator object
|
|
|
AbstractMetaArgumentList arguments = streamFunction->arguments();
|
|
|
arguments.removeFirst();
|
|
|
streamFunction->setArguments(arguments);
|
|
|
|
|
|
*streamFunction += AbstractMetaAttributes::Final;
|
|
|
*streamFunction += AbstractMetaAttributes::Public;
|
|
|
streamFunction->setOriginalAttributes(streamFunction->attributes());
|
|
|
|
|
|
setupFunctionDefaults(streamFunction, aClass);
|
|
|
|
|
|
aClass->addFunction(streamFunction);
|
|
|
if (bClass) {
|
|
|
aClass->typeEntry()->addExtraInclude(bClass->typeEntry()->include());
|
|
|
}
|
|
|
|
|
|
m_current_class = old_current_class;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::fixQObjectForScope(TypeDatabase *types,
|
|
|
NamespaceModelItem scope)
|
|
|
{
|
|
|
foreach (ClassModelItem item, scope->classes()) {
|
|
|
QString qualified_name = item->qualifiedName().join("::");
|
|
|
TypeEntry *entry = types->findType(qualified_name);
|
|
|
if (entry) {
|
|
|
if (isQObject(qualified_name) && entry->isComplex()) {
|
|
|
((ComplexTypeEntry *) entry)->setQObject(true);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
foreach (NamespaceModelItem item, scope->namespaceMap().values()) {
|
|
|
if (scope != item)
|
|
|
fixQObjectForScope(types, item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static bool class_less_than(AbstractMetaClass *a, AbstractMetaClass *b)
|
|
|
{
|
|
|
return a->name() < b->name();
|
|
|
}
|
|
|
|
|
|
|
|
|
void AbstractMetaBuilder::sortLists()
|
|
|
{
|
|
|
qSort(m_meta_classes.begin(), m_meta_classes.end(), class_less_than);
|
|
|
foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
|
cls->sortFunctions();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool AbstractMetaBuilder::build()
|
|
|
{
|
|
|
Q_ASSERT(!m_file_name.isEmpty());
|
|
|
|
|
|
QFile file(m_file_name);
|
|
|
|
|
|
if (!file.open(QFile::ReadOnly))
|
|
|
return false;
|
|
|
|
|
|
QTextStream stream(&file);
|
|
|
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
|
|
QByteArray contents = stream.readAll().toUtf8();
|
|
|
file.close();
|
|
|
|
|
|
Control control;
|
|
|
Parser p(&control);
|
|
|
pool __pool;
|
|
|
|
|
|
TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool);
|
|
|
|
|
|
CodeModel model;
|
|
|
Binder binder(&model, p.location());
|
|
|
m_dom = binder.run(ast);
|
|
|
|
|
|
pushScope(model_dynamic_cast<ScopeModelItem>(m_dom));
|
|
|
|
|
|
QHash<QString, ClassModelItem> typeMap = m_dom->classMap();
|
|
|
|
|
|
|
|
|
// fix up QObject's in the type system..
|
|
|
TypeDatabase *types = TypeDatabase::instance();
|
|
|
fixQObjectForScope(types, model_dynamic_cast<NamespaceModelItem>(m_dom));
|
|
|
|
|
|
|
|
|
// Start the generation...
|
|
|
foreach (ClassModelItem item, typeMap.values()) {
|
|
|
AbstractMetaClass *cls = traverseClass(item);
|
|
|
addAbstractMetaClass(cls);
|
|
|
}
|
|
|
|
|
|
|
|
|
QHash<QString, NamespaceModelItem> namespaceMap = m_dom->namespaceMap();
|
|
|
foreach (NamespaceModelItem item, namespaceMap.values()) {
|
|
|
AbstractMetaClass *meta_class = traverseNamespace(item);
|
|
|
if (meta_class)
|
|
|
m_meta_classes << meta_class;
|
|
|
}
|
|
|
|
|
|
|
|
|
// Some trickery to support global-namespace enums...
|
|
|
QHash<QString, EnumModelItem> enumMap = m_dom->enumMap();
|
|
|
m_current_class = 0;
|
|
|
foreach (EnumModelItem item, enumMap) {
|
|
|
AbstractMetaEnum *meta_enum = traverseEnum(item, 0, QSet<QString>());
|
|
|
|
|
|
if (meta_enum) {
|
|
|
QString package = meta_enum->typeEntry()->javaPackage();
|
|
|
QString globalName = TypeDatabase::globalNamespaceClassName(meta_enum->typeEntry());
|
|
|
|
|
|
AbstractMetaClass *global = m_meta_classes.findClass(package + "." + globalName);
|
|
|
if (!global) {
|
|
|
ComplexTypeEntry *gte = new ObjectTypeEntry(globalName);
|
|
|
gte->setTargetLangPackage(meta_enum->typeEntry()->javaPackage());
|
|
|
gte->setCodeGeneration(meta_enum->typeEntry()->codeGeneration());
|
|
|
global = createMetaClass();
|
|
|
global->setTypeEntry(gte);
|
|
|
*global += AbstractMetaAttributes::Final;
|
|
|
*global += AbstractMetaAttributes::Public;
|
|
|
*global += AbstractMetaAttributes::Fake;
|
|
|
|
|
|
m_meta_classes << global;
|
|
|
}
|
|
|
|
|
|
global->addEnum(meta_enum);
|
|
|
meta_enum->setEnclosingClass(global);
|
|
|
meta_enum->typeEntry()->setQualifier(globalName);
|
|
|
|
|
|
// Global enums should be public despite not having public
|
|
|
// identifiers so we'll fix the original attributes here.
|
|
|
meta_enum->setOriginalAttributes(meta_enum->attributes());
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Go through all typedefs to see if we have defined any
|
|
|
// specific typedefs to be used as classes.
|
|
|
TypeAliasList typeAliases = m_dom->typeAliases();
|
|
|
foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
|
AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
|
addAbstractMetaClass(cls);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
|
if (!cls->isInterface() && !cls->isNamespace()) {
|
|
|
setupInheritance(cls);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
|
cls->fixFunctions();
|
|
|
|
|
|
if (cls->typeEntry() == 0) {
|
|
|
ReportHandler::warning(QString("class '%1' does not have an entry in the type system")
|
|
|
.arg(cls->name()));
|
|
|
} else {
|
|
|
if (!cls->hasConstructors() && !cls->isFinalInCpp() && !cls->isInterface() && !cls->isNamespace())
|
|
|
cls->addDefaultConstructor();
|
|
|
}
|
|
|
|
|
|
if (cls->isAbstract() && !cls->isInterface()) {
|
|
|
cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + "$ConcreteWrapper");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QList<TypeEntry *> entries = TypeDatabase::instance()->entries().values();
|
|
|
foreach (const TypeEntry *entry, entries) {
|
|
|
if (entry->isPrimitive())
|
|
|
continue;
|
|
|
|
|
|
if ((entry->isValue() || entry->isObject())
|
|
|
&& !entry->isString()
|
|
|
&& !entry->isChar()
|
|
|
&& !entry->isContainer()
|
|
|
&& !entry->isCustom()
|
|
|
&& !entry->isVariant()
|
|
|
&& !m_meta_classes.findClass(entry->qualifiedCppName())) {
|
|
|
ReportHandler::warning(QString("type '%1' is specified in typesystem, but not defined. This could potentially lead to compilation errors.")
|
|
|
.arg(entry->qualifiedCppName()));
|
|
|
}
|
|
|
|
|
|
if (entry->isEnum()) {
|
|
|
QString pkg = entry->javaPackage();
|
|
|
QString name = (pkg.isEmpty() ? QString() : pkg + ".")
|
|
|
+ ((EnumTypeEntry *) entry)->javaQualifier();
|
|
|
AbstractMetaClass *cls = m_meta_classes.findClass(name);
|
|
|
|
|
|
if (!cls) {
|
|
|
ReportHandler::warning(QString("namespace '%1' for enum '%2' is not declared")
|
|
|
.arg(name).arg(entry->targetLangName()));
|
|
|
} else {
|
|
|
AbstractMetaEnum *e = cls->findEnum(entry->targetLangName());
|
|
|
if (!e)
|
|
|
ReportHandler::warning(QString("enum '%1' is specified in typesystem, "
|
|
|
"but not declared")
|
|
|
.arg(entry->qualifiedCppName()));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
{
|
|
|
FunctionList hash_functions = m_dom->findFunctions("qHash");
|
|
|
foreach (FunctionModelItem item, hash_functions) {
|
|
|
registerHashFunction(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
{
|
|
|
FunctionList hash_functions = m_dom->findFunctions("operator<<");
|
|
|
foreach (FunctionModelItem item, hash_functions) {
|
|
|
registerToStringCapability(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
{
|
|
|
FunctionList compare_operators = m_dom->findFunctions("operator==")
|
|
|
+ m_dom->findFunctions("operator<=")
|
|
|
+ m_dom->findFunctions("operator>=")
|
|
|
+ m_dom->findFunctions("operator<")
|
|
|
+ m_dom->findFunctions("operator>");
|
|
|
foreach (FunctionModelItem item, compare_operators) {
|
|
|
traverseCompareOperator(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
{
|
|
|
FunctionList stream_operators =
|
|
|
m_dom->findFunctions("operator+") + m_dom->findFunctions("operator-")
|
|
|
+ m_dom->findFunctions("operator/") + m_dom->findFunctions("operator*")
|
|
|
+ m_dom->findFunctions("operator&") + m_dom->findFunctions("operator|")
|
|
|
+ m_dom->findFunctions("operator%") + m_dom->findFunctions("operator^");
|
|
|
foreach (FunctionModelItem item, stream_operators) {
|
|
|
traverseBinaryArithmeticOperator(item);
|
|
|
}
|
|
|
}
|
|
|
{
|
|
|
FunctionList stream_operators = m_dom->findFunctions("operator<<") + m_dom->findFunctions("operator>>");
|
|
|
foreach (FunctionModelItem item, stream_operators) {
|
|
|
traverseStreamOperator(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
figureOutEnumValues();
|
|
|
figureOutDefaultEnumArguments();
|
|
|
checkFunctionModifications();
|
|
|
|
|
|
foreach (AbstractMetaClass *cls, m_meta_classes) {
|
|
|
setupEquals(cls);
|
|
|
setupComparable(cls);
|
|
|
setupClonable(cls);
|
|
|
}
|
|
|
|
|
|
dumpLog();
|
|
|
|
|
|
sortLists();
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
void AbstractMetaBuilder::addAbstractMetaClass(AbstractMetaClass *cls)
|
|
|
{
|
|
|
if (!cls)
|
|
|
return;
|
|
|
|
|
|
cls->setOriginalAttributes(cls->attributes());
|
|
|
if (cls->typeEntry()->isContainer()) {
|
|
|
m_templates << cls;
|
|
|
} else {
|
|
|
m_meta_classes << cls;
|
|
|
if (cls->typeEntry()->designatedInterface()) {
|
|
|
AbstractMetaClass *interface = cls->extractInterface();
|
|
|
m_meta_classes << interface;
|
|
|
ReportHandler::debugSparse(QString(" -> interface '%1'").arg(interface->name()));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
AbstractMetaClass *AbstractMetaBuilder::traverseNamespace(NamespaceModelItem namespace_item)
|
|
|
{
|
|
|
QString namespace_name = (!m_namespace_prefix.isEmpty() ? m_namespace_prefix + "::" : QString()) + namespace_item->name();
|
|
|
NamespaceTypeEntry *type = TypeDatabase::instance()->findNamespaceType(namespace_name);
|
|
|
|
|
|
if (TypeDatabase::instance()->isClassRejected(namespace_name)) {
|
|
|
m_rejected_classes.insert(namespace_name, GenerationDisabled);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (!type) {
|
|
|
ReportHandler::warning(QString("namespace '%1' does not have a type entry")
|
|
|
.arg(namespace_name));
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
AbstractMetaClass *meta_class = createMetaClass();
|
|
|
meta_class->setTypeEntry(type);
|
|
|
|
|
|
*meta_class += AbstractMetaAttributes::Public;
|
|
|
|
|
|
m_current_class = meta_class;
|
|
|
|
|
|
ReportHandler::debugSparse(QString("namespace '%1.%2'")
|
|
|
.arg(meta_class->package())
|
|
|
.arg(namespace_item->name()));
|
|
|
|
|
|
traverseEnums(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class, namespace_item->enumsDeclarations());
|
|
|
traverseFunctions(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class);
|
|
|
// traverseClasses(model_dynamic_cast<ScopeModelItem>(namespace_item));
|
|
|
|
|
|
pushScope(model_dynamic_cast<ScopeModelItem>(namespace_item));
|
|
|
m_namespace_prefix = currentScope()->qualifiedName().join("::");
|
|
|
|
|
|
|
|
|
ClassList classes = namespace_item->classes();
|
|
|
foreach (ClassModelItem cls, classes) {
|
|
|
AbstractMetaClass *mjc = traverseClass(cls);
|
|
|
addAbstractMetaClass(mjc);
|
|
|
}
|
|
|
|
|
|
// Go through all typedefs to see if we have defined any
|
|
|
// specific typedefs to be used as classes.
|
|
|
TypeAliasList typeAliases = namespace_item->typeAliases();
|
|
|
foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
|
AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
|
addAbstractMetaClass(cls);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Traverse namespaces recursively
|
|
|
QList<NamespaceModelItem> inner_namespaces = namespace_item->namespaceMap().values();
|
|
|
foreach (const NamespaceModelItem &ni, inner_namespaces) {
|
|
|
AbstractMetaClass *mjc = traverseNamespace(ni);
|
|
|
addAbstractMetaClass(mjc);
|
|
|
}
|
|
|
|
|
|
m_current_class = 0;
|
|
|
|
|
|
|
|
|
popScope();
|
|
|
m_namespace_prefix = currentScope()->qualifiedName().join("::");
|
|
|
|
|
|
if (!type->include().isValid()) {
|
|
|
QFileInfo info(namespace_item->fileName());
|
|
|
type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
|
}
|
|
|
|
|
|
return meta_class;
|
|
|
}
|
|
|
|
|
|
struct Operator
|
|
|
{
|
|
|
enum Type { Plus, ShiftLeft, None };
|
|
|
|
|
|
Operator() : type(None) { }
|
|
|
|
|
|
int calculate(int x) {
|
|
|
switch (type) {
|
|
|
case Plus: return x + value;
|
|
|
case ShiftLeft: return x << value;
|
|
|
case None: return x;
|
|
|
}
|
|
|
return x;
|
|
|
}
|
|
|
|
|
|
Type type;
|
|
|
int value;
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Operator findOperator(QString *s) {
|
|
|
const char *names[] = {
|
|
|
"+",
|
|
|
"<<"
|
|
|
};
|
|
|
|
|
|
for (int i=0; i<Operator::None; ++i) {
|
|
|
QString name = QLatin1String(names[i]);
|
|
|
QString str = *s;
|
|
|
int splitPoint = str.indexOf(name);
|
|
|
if (splitPoint > 0) {
|
|
|
bool ok;
|
|
|
QString right = str.mid(splitPoint + name.length());
|
|
|
Operator op;
|
|
|
op.value = right.toInt(&ok);
|
|
|
if (ok) {
|
|
|
op.type = Operator::Type(i);
|
|
|
*s = str.left(splitPoint).trimmed();
|
|
|
return op;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return Operator();
|
|
|
}
|
|
|
|
|
|
int AbstractMetaBuilder::figureOutEnumValue(const QString &stringValue,
|
|
|
int oldValuevalue,
|
|
|
AbstractMetaEnum *meta_enum,
|
|
|
AbstractMetaFunction *meta_function)
|
|
|
{
|
|
|
if (stringValue.isEmpty())
|
|
|
return oldValuevalue;
|
|
|
|
|
|
QStringList stringValues = stringValue.split("|");
|
|
|
|
|
|
int returnValue = 0;
|
|
|
|
|
|
bool matched = false;
|
|
|
|
|
|
for (int i=0; i<stringValues.size(); ++i) {
|
|
|
QString s = stringValues.at(i).trimmed();
|
|
|
|
|
|
bool ok;
|
|
|
int v;
|
|
|
|
|
|
Operator op = findOperator(&s);
|
|
|
|
|
|
if (s.length() > 0 && s.at(0) == QLatin1Char('0'))
|
|
|
v = s.toUInt(&ok, 0);
|
|
|
else
|
|
|
v = s.toInt(&ok);
|
|
|
|
|
|
if (ok) {
|
|
|
matched = true;
|
|
|
|
|
|
} else if (m_enum_values.contains(s)) {
|
|
|
v = m_enum_values[s]->value();
|
|
|
matched = true;
|
|
|
|
|
|
} else {
|
|
|
AbstractMetaEnumValue *ev = 0;
|
|
|
|
|
|
if (meta_enum && (ev = meta_enum->values().find(s))) {
|
|
|
v = ev->value();
|
|
|
matched = true;
|
|
|
|
|
|
} else if (meta_enum && (ev = meta_enum->enclosingClass()->findEnumValue(s, meta_enum))) {
|
|
|
v = ev->value();
|
|
|
matched = true;
|
|
|
|
|
|
} else {
|
|
|
if (meta_enum)
|
|
|
ReportHandler::warning("unhandled enum value: " + s + " in "
|
|
|
+ meta_enum->enclosingClass()->name() + "::"
|
|
|
+ meta_enum->name());
|
|
|
else
|
|
|
ReportHandler::warning("unhandled enum value: Unknown enum");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (matched)
|
|
|
returnValue |= op.calculate(v);
|
|
|
}
|
|
|
|
|
|
if (!matched) {
|
|
|
QString warn = QString("unmatched enum %1").arg(stringValue);
|
|
|
|
|
|
if (meta_function != 0) {
|
|
|
warn += QString(" when parsing default value of '%1' in class '%2'")
|
|
|
.arg(meta_function->name())
|
|
|
.arg(meta_function->implementingClass()->name());
|
|
|
}
|
|
|
|
|
|
ReportHandler::warning(warn);
|
|
|
returnValue = oldValuevalue;
|
|
|
}
|
|
|
|
|
|
return returnValue;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::figureOutEnumValuesForClass(AbstractMetaClass *meta_class,
|
|
|
QSet<AbstractMetaClass *> *classes)
|
|
|
{
|
|
|
AbstractMetaClass *base = meta_class->baseClass();
|
|
|
|
|
|
if (base != 0 && !classes->contains(base))
|
|
|
figureOutEnumValuesForClass(base, classes);
|
|
|
|
|
|
if (classes->contains(meta_class))
|
|
|
return;
|
|
|
|
|
|
AbstractMetaEnumList enums = meta_class->enums();
|
|
|
foreach (AbstractMetaEnum *e, enums) {
|
|
|
if (!e) {
|
|
|
ReportHandler::warning("bad enum in class " + meta_class->name());
|
|
|
continue;
|
|
|
}
|
|
|
AbstractMetaEnumValueList lst = e->values();
|
|
|
int value = 0;
|
|
|
for (int i=0; i<lst.size(); ++i) {
|
|
|
value = figureOutEnumValue(lst.at(i)->stringValue(), value, e);
|
|
|
lst.at(i)->setValue(value);
|
|
|
value++;
|
|
|
}
|
|
|
|
|
|
// Check for duplicate values...
|
|
|
EnumTypeEntry *ete = e->typeEntry();
|
|
|
if (!ete->forceInteger()) {
|
|
|
QHash<int, AbstractMetaEnumValue *> entries;
|
|
|
foreach (AbstractMetaEnumValue *v, lst) {
|
|
|
|
|
|
bool vRejected = ete->isEnumValueRejected(v->name());
|
|
|
|
|
|
AbstractMetaEnumValue *current = entries.value(v->value());
|
|
|
if (current) {
|
|
|
bool currentRejected = ete->isEnumValueRejected(current->name());
|
|
|
if (!currentRejected && !vRejected) {
|
|
|
ReportHandler::warning(
|
|
|
QString("duplicate enum values: %1::%2, %3 and %4 are %5, already rejected: (%6)")
|
|
|
.arg(meta_class->name())
|
|
|
.arg(e->name())
|
|
|
.arg(v->name())
|
|
|
.arg(entries[v->value()]->name())
|
|
|
.arg(v->value())
|
|
|
.arg(ete->enumValueRejections().join(", ")));
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!vRejected)
|
|
|
entries[v->value()] = v;
|
|
|
}
|
|
|
|
|
|
// Entries now contain all the original entries, no
|
|
|
// rejected ones... Use this to generate the enumValueRedirection table.
|
|
|
foreach (AbstractMetaEnumValue *reject, lst) {
|
|
|
if (!ete->isEnumValueRejected(reject->name()))
|
|
|
continue;
|
|
|
|
|
|
AbstractMetaEnumValue *used = entries.value(reject->value());
|
|
|
if (!used) {
|
|
|
ReportHandler::warning(
|
|
|
QString::fromLatin1("Rejected enum has no alternative...: %1::%2")
|
|
|
.arg(meta_class->name())
|
|
|
.arg(reject->name()));
|
|
|
continue;
|
|
|
}
|
|
|
ete->addEnumValueRedirection(reject->name(), used->name());
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*classes += meta_class;
|
|
|
}
|
|
|
|
|
|
|
|
|
void AbstractMetaBuilder::figureOutEnumValues()
|
|
|
{
|
|
|
// Keep a set of classes that we already traversed. We use this to
|
|
|
// enforce that we traverse base classes prior to subclasses.
|
|
|
QSet<AbstractMetaClass *> classes;
|
|
|
foreach (AbstractMetaClass *c, m_meta_classes) {
|
|
|
figureOutEnumValuesForClass(c, &classes);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::figureOutDefaultEnumArguments()
|
|
|
{
|
|
|
foreach (AbstractMetaClass *meta_class, m_meta_classes) {
|
|
|
foreach (AbstractMetaFunction *meta_function, meta_class->functions()) {
|
|
|
foreach (AbstractMetaArgument *arg, meta_function->arguments()) {
|
|
|
|
|
|
QString expr = arg->defaultValueExpression();
|
|
|
if (expr.isEmpty())
|
|
|
continue;
|
|
|
|
|
|
if (!meta_function->replacedDefaultExpression(meta_function->implementingClass(),
|
|
|
arg->argumentIndex()+1).isEmpty()) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QString new_expr = expr;
|
|
|
if (arg->type()->isEnum()) {
|
|
|
QStringList lst = expr.split(QLatin1String("::"));
|
|
|
if (lst.size() == 1) {
|
|
|
QVector<AbstractMetaClass *> classes(1, meta_class);
|
|
|
AbstractMetaEnum *e = 0;
|
|
|
while (!classes.isEmpty() && e == 0) {
|
|
|
if (classes.front() != 0) {
|
|
|
classes << classes.front()->baseClass();
|
|
|
|
|
|
AbstractMetaClassList interfaces = classes.front()->interfaces();
|
|
|
foreach (AbstractMetaClass *interface, interfaces)
|
|
|
classes << interface->primaryInterfaceImplementor();
|
|
|
|
|
|
e = classes.front()->findEnumForValue(expr);
|
|
|
}
|
|
|
|
|
|
classes.pop_front();
|
|
|
}
|
|
|
|
|
|
if (e != 0) {
|
|
|
new_expr = QString("%1.%2")
|
|
|
.arg(e->typeEntry()->qualifiedTargetLangName())
|
|
|
.arg(expr);
|
|
|
} else {
|
|
|
ReportHandler::warning("Cannot find enum constant for value '" + expr + "' in '" + meta_class->name() + "' or any of its super classes");
|
|
|
}
|
|
|
} else if (lst.size() == 2) {
|
|
|
AbstractMetaClass *cl = m_meta_classes.findClass(lst.at(0));
|
|
|
if (!cl) {
|
|
|
ReportHandler::warning("missing required class for enums: " + lst.at(0));
|
|
|
continue;
|
|
|
}
|
|
|
new_expr = QString("%1.%2.%3")
|
|
|
.arg(cl->typeEntry()->qualifiedTargetLangName())
|
|
|
.arg(arg->type()->name())
|
|
|
.arg(lst.at(1));
|
|
|
} else {
|
|
|
ReportHandler::warning("bad default value passed to enum " + expr);
|
|
|
}
|
|
|
|
|
|
} else if(arg->type()->isFlags()) {
|
|
|
const FlagsTypeEntry *flagsEntry =
|
|
|
static_cast<const FlagsTypeEntry *>(arg->type()->typeEntry());
|
|
|
EnumTypeEntry *enumEntry = flagsEntry->originator();
|
|
|
AbstractMetaEnum *meta_enum = m_meta_classes.findEnum(enumEntry);
|
|
|
if (!meta_enum) {
|
|
|
ReportHandler::warning("unknown required enum " + enumEntry->qualifiedCppName());
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
int value = figureOutEnumValue(expr, 0, meta_enum, meta_function);
|
|
|
new_expr = QString::number(value);
|
|
|
|
|
|
} else if (arg->type()->isPrimitive()) {
|
|
|
AbstractMetaEnumValue *value = 0;
|
|
|
if (expr.contains("::"))
|
|
|
value = m_meta_classes.findEnumValue(expr);
|
|
|
if (!value)
|
|
|
value = meta_class->findEnumValue(expr, 0);
|
|
|
|
|
|
if (value) {
|
|
|
new_expr = QString::number(value->value());
|
|
|
} else if (expr.contains(QLatin1Char('+'))) {
|
|
|
new_expr = QString::number(figureOutEnumValue(expr, 0, 0));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
arg->setDefaultValueExpression(new_expr);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
AbstractMetaEnum *AbstractMetaBuilder::traverseEnum(EnumModelItem enum_item, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations)
|
|
|
{
|
|
|
// Skipping private enums.
|
|
|
if (enum_item->accessPolicy() == CodeModel::Private) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
QString qualified_name = enum_item->qualifiedName().join("::");
|
|
|
|
|
|
TypeEntry *type_entry = TypeDatabase::instance()->findType(qualified_name);
|
|
|
QString enum_name = enum_item->name();
|
|
|
|
|
|
QString class_name;
|
|
|
if (m_current_class)
|
|
|
class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
|
|
|
|
if (TypeDatabase::instance()->isEnumRejected(class_name, enum_name)) {
|
|
|
m_rejected_enums.insert(qualified_name, GenerationDisabled);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (!type_entry || !type_entry->isEnum()) {
|
|
|
QString context = m_current_class ? m_current_class->name() : QLatin1String("");
|
|
|
ReportHandler::warning(QString("enum '%1' does not have a type entry or is not an enum")
|
|
|
.arg(qualified_name));
|
|
|
m_rejected_enums.insert(qualified_name, NotInTypeSystem);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
AbstractMetaEnum *meta_enum = createMetaEnum();
|
|
|
if ( enumsDeclarations.contains(qualified_name)
|
|
|
|| enumsDeclarations.contains(enum_name)) {
|
|
|
meta_enum->setHasQEnumsDeclaration(true);
|
|
|
}
|
|
|
|
|
|
meta_enum->setTypeEntry((EnumTypeEntry *) type_entry);
|
|
|
switch (enum_item->accessPolicy()) {
|
|
|
case CodeModel::Public: *meta_enum += AbstractMetaAttributes::Public; break;
|
|
|
case CodeModel::Protected: *meta_enum += AbstractMetaAttributes::Protected; break;
|
|
|
// case CodeModel::Private: *meta_enum += AbstractMetaAttributes::Private; break;
|
|
|
default: break;
|
|
|
}
|
|
|
|
|
|
ReportHandler::debugMedium(QString(" - traversing enum %1").arg(meta_enum->fullName()));
|
|
|
|
|
|
foreach (EnumeratorModelItem value, enum_item->enumerators()) {
|
|
|
|
|
|
AbstractMetaEnumValue *meta_enum_value = createMetaEnumValue();
|
|
|
meta_enum_value->setName(value->name());
|
|
|
// Deciding the enum value...
|
|
|
|
|
|
meta_enum_value->setStringValue(value->value());
|
|
|
meta_enum->addEnumValue(meta_enum_value);
|
|
|
|
|
|
ReportHandler::debugFull(" - " + meta_enum_value->name() + " = "
|
|
|
+ meta_enum_value->value());
|
|
|
|
|
|
// Add into global register...
|
|
|
if (enclosing)
|
|
|
m_enum_values[enclosing->name() + "::" + meta_enum_value->name()] = meta_enum_value;
|
|
|
else
|
|
|
m_enum_values[meta_enum_value->name()] = meta_enum_value;
|
|
|
}
|
|
|
|
|
|
QFileInfo info(enum_item->fileName());
|
|
|
meta_enum->typeEntry()->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
|
|
|
|
m_enums << meta_enum;
|
|
|
|
|
|
return meta_enum;
|
|
|
}
|
|
|
|
|
|
AbstractMetaClass *AbstractMetaBuilder::traverseTypeAlias(TypeAliasModelItem typeAlias)
|
|
|
{
|
|
|
QString class_name = strip_template_args(typeAlias->name());
|
|
|
|
|
|
QString full_class_name = class_name;
|
|
|
// we have an inner class
|
|
|
if (m_current_class) {
|
|
|
full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName())
|
|
|
+ "::" + full_class_name;
|
|
|
}
|
|
|
|
|
|
// If we haven't specified anything for the typedef, then we don't care
|
|
|
ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name);
|
|
|
if (type == 0)
|
|
|
return 0;
|
|
|
|
|
|
if (type->isObject())
|
|
|
static_cast<ObjectTypeEntry *>(type)->setQObject(isQObject(strip_template_args(typeAlias->type().qualifiedName().join("::"))));
|
|
|
|
|
|
AbstractMetaClass *meta_class = createMetaClass();
|
|
|
meta_class->setTypeAlias(true);
|
|
|
meta_class->setTypeEntry(type);
|
|
|
meta_class->setBaseClassNames(QStringList() << typeAlias->type().qualifiedName().join("::"));
|
|
|
*meta_class += AbstractMetaAttributes::Public;
|
|
|
|
|
|
// Set the default include file name
|
|
|
if (!type->include().isValid()) {
|
|
|
QFileInfo info(typeAlias->fileName());
|
|
|
type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
|
}
|
|
|
|
|
|
return meta_class;
|
|
|
}
|
|
|
|
|
|
AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem class_item)
|
|
|
{
|
|
|
QString class_name = strip_template_args(class_item->name());
|
|
|
QString full_class_name = class_name;
|
|
|
|
|
|
// we have inner an class
|
|
|
if (m_current_class) {
|
|
|
full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName())
|
|
|
+ "::" + full_class_name;
|
|
|
}
|
|
|
|
|
|
ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name);
|
|
|
RejectReason reason = NoReason;
|
|
|
|
|
|
if (full_class_name == "QMetaTypeId") {
|
|
|
// QtScript: record which types have been declared
|
|
|
int lpos = class_item->name().indexOf('<');
|
|
|
int rpos = class_item->name().lastIndexOf('>');
|
|
|
if ((lpos != -1) && (rpos != -1)) {
|
|
|
QString declared_typename = class_item->name().mid(lpos+1, rpos - lpos-1);
|
|
|
m_qmetatype_declared_typenames.insert(declared_typename);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (TypeDatabase::instance()->isClassRejected(full_class_name)) {
|
|
|
reason = GenerationDisabled;
|
|
|
} else if (!type) {
|
|
|
TypeEntry *te = TypeDatabase::instance()->findType(full_class_name);
|
|
|
if (te && !te->isComplex())
|
|
|
reason = RedefinedToNotClass;
|
|
|
else
|
|
|
reason = NotInTypeSystem;
|
|
|
} else if (type->codeGeneration() == TypeEntry::GenerateNothing) {
|
|
|
reason = GenerationDisabled;
|
|
|
}
|
|
|
|
|
|
if (reason != NoReason) {
|
|
|
m_rejected_classes.insert(full_class_name, reason);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (type->isObject()) {
|
|
|
((ObjectTypeEntry *)type)->setQObject(isQObject(full_class_name));
|
|
|
}
|
|
|
|
|
|
AbstractMetaClass *meta_class = createMetaClass();
|
|
|
meta_class->setTypeEntry(type);
|
|
|
meta_class->setBaseClassNames(class_item->baseClasses());
|
|
|
*meta_class += AbstractMetaAttributes::Public;
|
|
|
|
|
|
AbstractMetaClass *old_current_class = m_current_class;
|
|
|
m_current_class = meta_class;
|
|
|
|
|
|
if (type->isContainer()) {
|
|
|
ReportHandler::debugSparse(QString("container: '%1'").arg(full_class_name));
|
|
|
} else {
|
|
|
ReportHandler::debugSparse(QString("class: '%1'").arg(meta_class->fullName()));
|
|
|
}
|
|
|
|
|
|
TemplateParameterList template_parameters = class_item->templateParameters();
|
|
|
QList<TypeEntry *> template_args;
|
|
|
template_args.clear();
|
|
|
for (int i=0; i<template_parameters.size(); ++i) {
|
|
|
const TemplateParameterModelItem ¶m = template_parameters.at(i);
|
|
|
TemplateArgumentEntry *param_type = new TemplateArgumentEntry(param->name());
|
|
|
param_type->setOrdinal(i);
|
|
|
template_args.append(param_type);
|
|
|
}
|
|
|
meta_class->setTemplateArguments(template_args);
|
|
|
|
|
|
parseQ_Property(meta_class, class_item->propertyDeclarations());
|
|
|
|
|
|
traverseFunctions(model_dynamic_cast<ScopeModelItem>(class_item), meta_class);
|
|
|
traverseEnums(model_dynamic_cast<ScopeModelItem>(class_item), meta_class, class_item->enumsDeclarations());
|
|
|
traverseFields(model_dynamic_cast<ScopeModelItem>(class_item), meta_class);
|
|
|
|
|
|
// Inner classes
|
|
|
{
|
|
|
QList<ClassModelItem> inner_classes = class_item->classMap().values();
|
|
|
foreach (const ClassModelItem &ci, inner_classes) {
|
|
|
AbstractMetaClass *cl = traverseClass(ci);
|
|
|
if (cl) {
|
|
|
cl->setEnclosingClass(meta_class);
|
|
|
m_meta_classes << cl;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Go through all typedefs to see if we have defined any
|
|
|
// specific typedefs to be used as classes.
|
|
|
TypeAliasList typeAliases = class_item->typeAliases();
|
|
|
foreach (TypeAliasModelItem typeAlias, typeAliases) {
|
|
|
AbstractMetaClass *cls = traverseTypeAlias(typeAlias);
|
|
|
if (cls != 0) {
|
|
|
cls->setEnclosingClass(meta_class);
|
|
|
addAbstractMetaClass(cls);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
m_current_class = old_current_class;
|
|
|
|
|
|
// Set the default include file name
|
|
|
if (!type->include().isValid()) {
|
|
|
QFileInfo info(class_item->fileName());
|
|
|
type->setInclude(Include(Include::IncludePath, info.fileName()));
|
|
|
}
|
|
|
|
|
|
return meta_class;
|
|
|
}
|
|
|
|
|
|
AbstractMetaField *AbstractMetaBuilder::traverseField(VariableModelItem field, const AbstractMetaClass *cls)
|
|
|
{
|
|
|
QString field_name = field->name();
|
|
|
QString class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
|
|
|
|
// Ignore friend decl.
|
|
|
if (field->isFriend())
|
|
|
return 0;
|
|
|
|
|
|
if (field->accessPolicy() == CodeModel::Private)
|
|
|
return 0;
|
|
|
|
|
|
if (TypeDatabase::instance()->isFieldRejected(class_name, field_name)) {
|
|
|
m_rejected_fields.insert(class_name + "::" + field_name, GenerationDisabled);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
AbstractMetaField *meta_field = createMetaField();
|
|
|
meta_field->setName(field_name);
|
|
|
meta_field->setEnclosingClass(cls);
|
|
|
|
|
|
bool ok;
|
|
|
TypeInfo field_type = field->type();
|
|
|
AbstractMetaType *meta_type = translateType(field_type, &ok);
|
|
|
|
|
|
if (!meta_type || !ok) {
|
|
|
ReportHandler::warning(QString("skipping field '%1::%2' with unmatched type '%3'")
|
|
|
.arg(m_current_class->name())
|
|
|
.arg(field_name)
|
|
|
.arg(TypeInfo::resolveType(field_type, currentScope()->toItem()).qualifiedName().join("::")));
|
|
|
delete meta_field;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
meta_field->setType(meta_type);
|
|
|
|
|
|
uint attr = 0;
|
|
|
if (field->isStatic())
|
|
|
attr |= AbstractMetaAttributes::Static;
|
|
|
|
|
|
CodeModel::AccessPolicy policy = field->accessPolicy();
|
|
|
if (policy == CodeModel::Public)
|
|
|
attr |= AbstractMetaAttributes::Public;
|
|
|
else if (policy == CodeModel::Protected)
|
|
|
attr |= AbstractMetaAttributes::Protected;
|
|
|
else
|
|
|
attr |= AbstractMetaAttributes::Private;
|
|
|
meta_field->setAttributes(attr);
|
|
|
|
|
|
return meta_field;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseFields(ScopeModelItem scope_item, AbstractMetaClass *meta_class)
|
|
|
{
|
|
|
foreach (VariableModelItem field, scope_item->variables()) {
|
|
|
AbstractMetaField *meta_field = traverseField(field, meta_class);
|
|
|
|
|
|
if (meta_field) {
|
|
|
meta_field->setOriginalAttributes(meta_field->attributes());
|
|
|
meta_class->addField(meta_field);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::setupFunctionDefaults(AbstractMetaFunction *meta_function, AbstractMetaClass *meta_class)
|
|
|
{
|
|
|
// Set the default value of the declaring class. This may be changed
|
|
|
// in fixFunctions later on
|
|
|
meta_function->setDeclaringClass(meta_class);
|
|
|
|
|
|
// Some of the queries below depend on the implementing class being set
|
|
|
// to function properly. Such as function modifications
|
|
|
meta_function->setImplementingClass(meta_class);
|
|
|
|
|
|
if (meta_function->name() == "operator_equal")
|
|
|
meta_class->setHasEqualsOperator(true);
|
|
|
|
|
|
if (!meta_function->isFinalInTargetLang()
|
|
|
&& meta_function->isRemovedFrom(meta_class, TypeSystem::TargetLangCode)) {
|
|
|
*meta_function += AbstractMetaAttributes::FinalInCpp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scope_item, AbstractMetaClass *meta_class)
|
|
|
{
|
|
|
foreach (FunctionModelItem function, scope_item->functions()) {
|
|
|
AbstractMetaFunction *meta_function = traverseFunction(function);
|
|
|
|
|
|
if (meta_function) {
|
|
|
meta_function->setOriginalAttributes(meta_function->attributes());
|
|
|
if (meta_class->isNamespace())
|
|
|
*meta_function += AbstractMetaAttributes::Static;
|
|
|
|
|
|
if (QPropertySpec *read = meta_class->propertySpecForRead(meta_function->name())) {
|
|
|
if (read->type() == meta_function->type()->typeEntry()) {
|
|
|
*meta_function += AbstractMetaAttributes::PropertyReader;
|
|
|
meta_function->setPropertySpec(read);
|
|
|
// printf("%s is reader for %s\n",
|
|
|
// qPrintable(meta_function->name()),
|
|
|
// qPrintable(read->name()));
|
|
|
}
|
|
|
} else if (QPropertySpec *write =
|
|
|
meta_class->propertySpecForWrite(meta_function->name())) {
|
|
|
if (write->type() == meta_function->arguments().at(0)->type()->typeEntry()) {
|
|
|
*meta_function += AbstractMetaAttributes::PropertyWriter;
|
|
|
meta_function->setPropertySpec(write);
|
|
|
// printf("%s is writer for %s\n",
|
|
|
// qPrintable(meta_function->name()),
|
|
|
// qPrintable(write->name()));
|
|
|
}
|
|
|
} else if (QPropertySpec *reset =
|
|
|
meta_class->propertySpecForReset(meta_function->name())) {
|
|
|
*meta_function += AbstractMetaAttributes::PropertyResetter;
|
|
|
meta_function->setPropertySpec(reset);
|
|
|
// printf("%s is resetter for %s\n",
|
|
|
// qPrintable(meta_function->name()),
|
|
|
// qPrintable(reset->name()));
|
|
|
}
|
|
|
|
|
|
|
|
|
bool isInvalidDestructor = meta_function->isDestructor() && meta_function->isPrivate();
|
|
|
bool isInvalidConstructor = meta_function->isConstructor()
|
|
|
&& (meta_function->isPrivate() || meta_function->isInvalid());
|
|
|
if ((isInvalidDestructor || isInvalidConstructor)
|
|
|
&& !meta_class->hasNonPrivateConstructor()) {
|
|
|
*meta_class += AbstractMetaAttributes::Final;
|
|
|
} else if (meta_function->isConstructor() && !meta_function->isPrivate()) {
|
|
|
*meta_class -= AbstractMetaAttributes::Final;
|
|
|
meta_class->setHasNonPrivateConstructor(true);
|
|
|
}
|
|
|
|
|
|
// Classes with virtual destructors should always have a shell class
|
|
|
// (since we aren't registering the destructors, we need this extra check)
|
|
|
if (meta_function->isDestructor() && !meta_function->isFinal())
|
|
|
meta_class->setForceShellClass(true);
|
|
|
|
|
|
if (!meta_function->isDestructor()
|
|
|
&& !meta_function->isInvalid()
|
|
|
&& (!meta_function->isConstructor() || !meta_function->isPrivate())) {
|
|
|
|
|
|
if (meta_class->typeEntry()->designatedInterface() && !meta_function->isPublic()
|
|
|
&& !meta_function->isPrivate()) {
|
|
|
QString warn = QString("non-public function '%1' in interface '%2'")
|
|
|
.arg(meta_function->name()).arg(meta_class->name());
|
|
|
ReportHandler::warning(warn);
|
|
|
|
|
|
meta_function->setVisibility(AbstractMetaClass::Public);
|
|
|
}
|
|
|
|
|
|
setupFunctionDefaults(meta_function, meta_class);
|
|
|
|
|
|
if (meta_function->isSignal() && meta_class->hasSignal(meta_function)) {
|
|
|
QString warn = QString("signal '%1' in class '%2' is overloaded.")
|
|
|
.arg(meta_function->name()).arg(meta_class->name());
|
|
|
ReportHandler::warning(warn);
|
|
|
}
|
|
|
|
|
|
if (meta_function->isSignal() && !meta_class->isQObject()) {
|
|
|
QString warn = QString("signal '%1' in non-QObject class '%2'")
|
|
|
.arg(meta_function->name()).arg(meta_class->name());
|
|
|
ReportHandler::warning(warn);
|
|
|
}
|
|
|
|
|
|
meta_class->addFunction(meta_function);
|
|
|
} else if (meta_function->isDestructor() && !meta_function->isPublic()) {
|
|
|
meta_class->setHasPublicDestructor(false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool AbstractMetaBuilder::setupInheritance(AbstractMetaClass *meta_class)
|
|
|
{
|
|
|
Q_ASSERT(!meta_class->isInterface());
|
|
|
|
|
|
if (m_setup_inheritance_done.contains(meta_class))
|
|
|
return true;
|
|
|
m_setup_inheritance_done.insert(meta_class);
|
|
|
|
|
|
QStringList base_classes = meta_class->baseClassNames();
|
|
|
|
|
|
TypeDatabase *types = TypeDatabase::instance();
|
|
|
|
|
|
// we only support our own containers and ONLY if there is only one baseclass
|
|
|
if (base_classes.size() == 1 && base_classes.first().count('<') == 1) {
|
|
|
QStringList scope = meta_class->typeEntry()->qualifiedCppName().split("::");
|
|
|
scope.removeLast();
|
|
|
for (int i=scope.size(); i>=0; --i) {
|
|
|
QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join("::") + "::" : QString();
|
|
|
QString complete_name = prefix + base_classes.first();
|
|
|
TypeParser::Info info = TypeParser::parse(complete_name);
|
|
|
QString base_name = info.qualified_name.join("::");
|
|
|
|
|
|
AbstractMetaClass *templ = 0;
|
|
|
foreach (AbstractMetaClass *c, m_templates) {
|
|
|
if (c->typeEntry()->name() == base_name) {
|
|
|
templ = c;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (templ == 0)
|
|
|
templ = m_meta_classes.findClass(base_name);
|
|
|
|
|
|
if (templ) {
|
|
|
setupInheritance(templ);
|
|
|
inheritTemplate(meta_class, templ, info);
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ReportHandler::warning(QString("template baseclass '%1' of '%2' is not known")
|
|
|
.arg(base_classes.first())
|
|
|
.arg(meta_class->name()));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
int primary = -1;
|
|
|
int primaries = 0;
|
|
|
for (int i=0; i<base_classes.size(); ++i) {
|
|
|
|
|
|
if (types->isClassRejected(base_classes.at(i)))
|
|
|
continue;
|
|
|
|
|
|
TypeEntry *base_class_entry = types->findType(base_classes.at(i));
|
|
|
if (!base_class_entry) {
|
|
|
ReportHandler::warning(QString("class '%1' inherits from unknown base class '%2'")
|
|
|
.arg(meta_class->name()).arg(base_classes.at(i)));
|
|
|
}
|
|
|
|
|
|
// true for primary base class
|
|
|
else if (!base_class_entry->designatedInterface()) {
|
|
|
if (primaries > 0) {
|
|
|
ReportHandler::warning(QString("class '%1' has multiple primary base classes"
|
|
|
" '%2' and '%3'")
|
|
|
.arg(meta_class->name())
|
|
|
.arg(base_classes.at(primary))
|
|
|
.arg(base_class_entry->name()));
|
|
|
return false;
|
|
|
}
|
|
|
primaries++;
|
|
|
primary = i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (primary >= 0) {
|
|
|
AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(primary));
|
|
|
if (!base_class) {
|
|
|
ReportHandler::warning(QString("unknown baseclass for '%1': '%2'")
|
|
|
.arg(meta_class->name())
|
|
|
.arg(base_classes.at(primary)));
|
|
|
return false;
|
|
|
}
|
|
|
meta_class->setBaseClass(base_class);
|
|
|
|
|
|
if (meta_class->typeEntry()->designatedInterface() != 0 && meta_class->isQObject()) {
|
|
|
ReportHandler::warning(QString("QObject extended by interface type '%1'. This is not supported and the generated Java code will not compile.")
|
|
|
.arg(meta_class->name()));
|
|
|
} else if (meta_class->typeEntry()->designatedInterface() != 0 && base_class != 0 && !base_class->isInterface()) {
|
|
|
ReportHandler::warning(QString("object type '%1' extended by interface type '%2'. The resulting API will be less expressive than the original.")
|
|
|
.arg(base_class->name())
|
|
|
.arg(meta_class->name()));
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int i=0; i<base_classes.size(); ++i) {
|
|
|
if (types->isClassRejected(base_classes.at(i)))
|
|
|
continue;
|
|
|
|
|
|
if (i != primary) {
|
|
|
AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(i));
|
|
|
if (base_class == 0) {
|
|
|
ReportHandler::warning(QString("class not found for setup inheritance '%1'").arg(base_classes.at(i)));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
setupInheritance(base_class);
|
|
|
|
|
|
QString interface_name = InterfaceTypeEntry::interfaceName(base_class->name());
|
|
|
AbstractMetaClass *iface = m_meta_classes.findClass(interface_name);
|
|
|
if (!iface) {
|
|
|
ReportHandler::warning(QString("unknown interface for '%1': '%2'")
|
|
|
.arg(meta_class->name())
|
|
|
.arg(interface_name));
|
|
|
return false;
|
|
|
}
|
|
|
meta_class->addInterface(iface);
|
|
|
|
|
|
AbstractMetaClassList interfaces = iface->interfaces();
|
|
|
foreach (AbstractMetaClass *iface, interfaces)
|
|
|
meta_class->addInterface(iface);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::traverseEnums(ScopeModelItem scope_item, AbstractMetaClass *meta_class, const QStringList &enumsDeclarations)
|
|
|
{
|
|
|
EnumList enums = scope_item->enums();
|
|
|
foreach (EnumModelItem enum_item, enums) {
|
|
|
AbstractMetaEnum *meta_enum = traverseEnum(enum_item, meta_class, QSet<QString>::fromList(enumsDeclarations));
|
|
|
if (meta_enum) {
|
|
|
meta_enum->setOriginalAttributes(meta_enum->attributes());
|
|
|
meta_class->addEnum(meta_enum);
|
|
|
meta_enum->setEnclosingClass(meta_class);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem function_item)
|
|
|
{
|
|
|
QString function_name = function_item->name();
|
|
|
QString class_name = m_current_class->typeEntry()->qualifiedCppName();
|
|
|
|
|
|
if (TypeDatabase::instance()->isFunctionRejected(class_name, function_name)) {
|
|
|
m_rejected_functions.insert(class_name + "::" + function_name, GenerationDisabled);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
Q_ASSERT(function_item->functionType() == CodeModel::Normal
|
|
|
|| function_item->functionType() == CodeModel::Signal
|
|
|
|| function_item->functionType() == CodeModel::Slot);
|
|
|
|
|
|
if (function_item->isFriend())
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
QString cast_type;
|
|
|
|
|
|
if (function_name.startsWith("operator")) {
|
|
|
function_name = rename_operator(function_name.mid(8));
|
|
|
if (function_name.isEmpty()) {
|
|
|
m_rejected_functions.insert(class_name + "::" + function_name,
|
|
|
GenerationDisabled);
|
|
|
return 0;
|
|
|
}
|
|
|
if (function_name.contains("_cast_"))
|
|
|
cast_type = function_name.mid(14).trimmed();
|
|
|
}
|
|
|
|
|
|
AbstractMetaFunction *meta_function = createMetaFunction();
|
|
|
meta_function->setConstant(function_item->isConstant());
|
|
|
|
|
|
ReportHandler::debugMedium(QString(" - %2()").arg(function_name));
|
|
|
|
|
|
meta_function->setName(function_name);
|
|
|
meta_function->setOriginalName(function_item->name());
|
|
|
|
|
|
if (function_item->isAbstract())
|
|
|
*meta_function += AbstractMetaAttributes::Abstract;
|
|
|
|
|
|
if (!meta_function->isAbstract())
|
|
|
*meta_function += AbstractMetaAttributes::Native;
|
|
|
|
|
|
if (!function_item->isVirtual())
|
|
|
*meta_function += AbstractMetaAttributes::Final;
|
|
|
|
|
|
if (function_item->isInvokable())
|
|
|
*meta_function += AbstractMetaAttributes::Invokable;
|
|
|
|
|
|
if (function_item->isStatic()) {
|
|
|
*meta_function += AbstractMetaAttributes::Static;
|
|
|
*meta_function += AbstractMetaAttributes::Final;
|
|
|
}
|
|
|
|
|
|
// Access rights
|
|
|
if (function_item->accessPolicy() == CodeModel::Public)
|
|
|
*meta_function += AbstractMetaAttributes::Public;
|
|
|
else if (function_item->accessPolicy() == CodeModel::Private)
|
|
|
*meta_function += AbstractMetaAttributes::Private;
|
|
|
else
|
|
|
*meta_function += AbstractMetaAttributes::Protected;
|
|
|
|
|
|
|
|
|
QString stripped_class_name = class_name;
|
|
|
int cc_pos = stripped_class_name.lastIndexOf("::");
|
|
|
if (cc_pos > 0)
|
|
|
stripped_class_name = stripped_class_name.mid(cc_pos + 2);
|
|
|
|
|
|
TypeInfo function_type = function_item->type();
|
|
|
if (function_name.startsWith('~')) {
|
|
|
meta_function->setFunctionType(AbstractMetaFunction::DestructorFunction);
|
|
|
meta_function->setInvalid(true);
|
|
|
} else if (strip_template_args(function_name) == stripped_class_name) {
|
|
|
meta_function->setFunctionType(AbstractMetaFunction::ConstructorFunction);
|
|
|
meta_function->setName(m_current_class->name());
|
|
|
} else {
|
|
|
bool ok;
|
|
|
AbstractMetaType *type = 0;
|
|
|
|
|
|
if (!cast_type.isEmpty()) {
|
|
|
TypeInfo info;
|
|
|
info.setQualifiedName(QStringList(cast_type));
|
|
|
type = translateType(info, &ok);
|
|
|
} else {
|
|
|
type = translateType(function_type, &ok);
|
|
|
}
|
|
|
|
|
|
if (!ok) {
|
|
|
ReportHandler::warning(QString("skipping function '%1::%2', unmatched return type '%3'")
|
|
|
.arg(class_name)
|
|
|
.arg(function_item->name())
|
|
|
.arg(function_item->type().toString()));
|
|
|
m_rejected_functions[class_name + "::" + function_name] =
|
|
|
UnmatchedReturnType;
|
|
|
meta_function->setInvalid(true);
|
|
|
return meta_function;
|
|
|
}
|
|
|
meta_function->setType(type);
|
|
|
|
|
|
if (function_item->functionType() == CodeModel::Signal)
|
|
|
meta_function->setFunctionType(AbstractMetaFunction::SignalFunction);
|
|
|
else if (function_item->functionType() == CodeModel::Slot)
|
|
|
meta_function->setFunctionType(AbstractMetaFunction::SlotFunction);
|
|
|
}
|
|
|
|
|
|
ArgumentList arguments = function_item->arguments();
|
|
|
AbstractMetaArgumentList meta_arguments;
|
|
|
|
|
|
int first_default_argument = 0;
|
|
|
for (int i=0; i<arguments.size(); ++i) {
|
|
|
ArgumentModelItem arg = arguments.at(i);
|
|
|
|
|
|
bool ok;
|
|
|
AbstractMetaType *meta_type = translateType(arg->type(), &ok);
|
|
|
if (!meta_type || !ok) {
|
|
|
ReportHandler::warning(QString("skipping function '%1::%2', "
|
|
|
"unmatched parameter type '%3'")
|
|
|
.arg(class_name)
|
|
|
.arg(function_item->name())
|
|
|
.arg(arg->type().toString()));
|
|
|
m_rejected_functions[class_name + "::" + function_name] =
|
|
|
UnmatchedArgumentType;
|
|
|
meta_function->setInvalid(true);
|
|
|
return meta_function;
|
|
|
}
|
|
|
AbstractMetaArgument *meta_argument = createMetaArgument();
|
|
|
meta_argument->setType(meta_type);
|
|
|
meta_argument->setName(arg->name());
|
|
|
meta_argument->setArgumentIndex(i);
|
|
|
meta_arguments << meta_argument;
|
|
|
}
|
|
|
|
|
|
meta_function->setArguments(meta_arguments);
|
|
|
|
|
|
// Find the correct default values
|
|
|
for (int i=0; i<arguments.size(); ++i) {
|
|
|
ArgumentModelItem arg = arguments.at(i);
|
|
|
AbstractMetaArgument *meta_arg = meta_arguments.at(i);
|
|
|
if (arg->defaultValue()) {
|
|
|
QString expr = arg->defaultValueExpression();
|
|
|
if (!expr.isEmpty())
|
|
|
meta_arg->setOriginalDefaultValueExpression(expr);
|
|
|
|
|
|
expr = translateDefaultValue(arg, meta_arg->type(), meta_function, m_current_class, i);
|
|
|
if (expr.isEmpty()) {
|
|
|
first_default_argument = i;
|
|
|
} else {
|
|
|
meta_arg->setDefaultValueExpression(expr);
|
|
|
}
|
|
|
|
|
|
if (meta_arg->type()->isEnum() || meta_arg->type()->isFlags()) {
|
|
|
m_enum_default_arguments
|
|
|
<< QPair<AbstractMetaArgument *, AbstractMetaFunction *>(meta_arg, meta_function);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// If we where not able to translate the default argument make it
|
|
|
// reset all default arguments before this one too.
|
|
|
for (int i=0; i<first_default_argument; ++i)
|
|
|
meta_arguments[i]->setDefaultValueExpression(QString());
|
|
|
|
|
|
if (ReportHandler::debugLevel() == ReportHandler::FullDebug)
|
|
|
foreach(AbstractMetaArgument *arg, meta_arguments)
|
|
|
ReportHandler::debugFull(" - " + arg->toString());
|
|
|
|
|
|
return meta_function;
|
|
|
}
|
|
|
|
|
|
|
|
|
AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, bool *ok, bool resolveType, bool resolveScope)
|
|
|
{
|
|
|
Q_ASSERT(ok);
|
|
|
*ok = true;
|
|
|
|
|
|
// 1. Test the type info without resolving typedefs in case this is present in the
|
|
|
// type system
|
|
|
TypeInfo typei;
|
|
|
if (resolveType) {
|
|
|
bool isok;
|
|
|
AbstractMetaType *t = translateType(_typei, &isok, false, resolveScope);
|
|
|
if (t != 0 && isok)
|
|
|
return t;
|
|
|
}
|
|
|
|
|
|
if (!resolveType)
|
|
|
typei = _typei;
|
|
|
else {
|
|
|
// Go through all parts of the current scope (including global namespace)
|
|
|
// to resolve typedefs. The parser does not properly resolve typedefs in
|
|
|
// the global scope when they are referenced from inside a namespace.
|
|
|
// This is a work around to fix this bug since fixing it in resolveType
|
|
|
// seemed non-trivial
|
|
|
int i = m_scopes.size() - 1;
|
|
|
while (i >= 0) {
|
|
|
typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)->toItem());
|
|
|
if (typei.qualifiedName().join("::") != _typei.qualifiedName().join("::"))
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typei.isFunctionPointer()) {
|
|
|
*ok = false;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
TypeParser::Info typeInfo = TypeParser::parse(typei.toString());
|
|
|
if (typeInfo.is_busted) {
|
|
|
*ok = false;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 2. Handle pointers specified as arrays with unspecified size
|
|
|
bool array_of_unspecified_size = false;
|
|
|
if (typeInfo.arrays.size() > 0) {
|
|
|
array_of_unspecified_size = true;
|
|
|
for (int i=0; i<typeInfo.arrays.size(); ++i)
|
|
|
array_of_unspecified_size = array_of_unspecified_size && typeInfo.arrays.at(i).isEmpty();
|
|
|
|
|
|
if (!array_of_unspecified_size) {
|
|
|
TypeInfo newInfo;
|
|
|
//newInfo.setArguments(typei.arguments());
|
|
|
newInfo.setIndirections(typei.indirections());
|
|
|
newInfo.setConstant(typei.isConstant());
|
|
|
newInfo.setFunctionPointer(typei.isFunctionPointer());
|
|
|
newInfo.setQualifiedName(typei.qualifiedName());
|
|
|
newInfo.setReference(typei.isReference());
|
|
|
newInfo.setVolatile(typei.isVolatile());
|
|
|
|
|
|
AbstractMetaType *elementType = translateType(newInfo, ok);
|
|
|
if (!(*ok))
|
|
|
return 0;
|
|
|
|
|
|
for (int i=typeInfo.arrays.size()-1; i>=0; --i) {
|
|
|
QString s = typeInfo.arrays.at(i);
|
|
|
bool isok;
|
|
|
|
|
|
int elems = s.toInt(&isok);
|
|
|
if (!isok)
|
|
|
return 0;
|
|
|
|
|
|
AbstractMetaType *arrayType = createMetaType();
|
|
|
arrayType->setArrayElementCount(elems);
|
|
|
arrayType->setArrayElementType(elementType);
|
|
|
arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry()));
|
|
|
decideUsagePattern(arrayType);
|
|
|
|
|
|
elementType = arrayType;
|
|
|
}
|
|
|
|
|
|
return elementType;
|
|
|
} else {
|
|
|
typeInfo.indirections += typeInfo.arrays.size();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QStringList qualifier_list = typeInfo.qualified_name;
|
|
|
if (qualifier_list.isEmpty()) {
|
|
|
ReportHandler::warning(QString("horribly broken type '%1'").arg(_typei.toString()));
|
|
|
*ok = false;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
QString qualified_name = qualifier_list.join("::");
|
|
|
QString name = qualifier_list.takeLast();
|
|
|
|
|
|
// 3. Special case 'void' type
|
|
|
if (name == "void" && typeInfo.indirections == 0) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 4. Special case QFlags (include instantiation in name)
|
|
|
if (qualified_name == "QFlags")
|
|
|
qualified_name = typeInfo.toString();
|
|
|
|
|
|
// 5. Try to find the type
|
|
|
const TypeEntry *type = TypeDatabase::instance()->findType(qualified_name);
|
|
|
|
|
|
// 6. No? Try looking it up as a flags type
|
|
|
if (!type)
|
|
|
type = TypeDatabase::instance()->findFlagsType(qualified_name);
|
|
|
|
|
|
// 7. No? Try looking it up as a container type
|
|
|
if (!type)
|
|
|
type = TypeDatabase::instance()->findContainerType(name);
|
|
|
|
|
|
// 8. No? Check if the current class is a template and this type is one
|
|
|
// of the parameters.
|
|
|
if (type == 0 && m_current_class != 0) {
|
|
|
QList<TypeEntry *> template_args = m_current_class->templateArguments();
|
|
|
foreach (TypeEntry *te, template_args) {
|
|
|
if (te->name() == qualified_name)
|
|
|
type = te;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 9. Try finding the type by prefixing it with the current
|
|
|
// context and all baseclasses of the current context
|
|
|
if (!type && !TypeDatabase::instance()->isClassRejected(qualified_name) && m_current_class != 0 && resolveScope) {
|
|
|
QStringList contexts;
|
|
|
contexts.append(m_current_class->qualifiedCppName());
|
|
|
contexts.append(currentScope()->qualifiedName().join("::"));
|
|
|
|
|
|
|
|
|
TypeInfo info = typei;
|
|
|
bool subclasses_done = false;
|
|
|
while (!contexts.isEmpty() && type == 0) {
|
|
|
//type = TypeDatabase::instance()->findType(contexts.at(0) + "::" + qualified_name);
|
|
|
|
|
|
bool isok;
|
|
|
info.setQualifiedName(QStringList() << contexts.at(0) << qualified_name);
|
|
|
AbstractMetaType *t = translateType(info, &isok, true, false);
|
|
|
if (t != 0 && isok)
|
|
|
return t;
|
|
|
|
|
|
ClassModelItem item = m_dom->findClass(contexts.at(0));
|
|
|
if (item != 0)
|
|
|
contexts += item->baseClasses();
|
|
|
contexts.pop_front();
|
|
|
|
|
|
// 10. Last resort: Special cased prefix of Qt namespace since the meta object implicitly inherits this, so
|
|
|
// enum types from there may be addressed without any scope resolution in properties.
|
|
|
if (contexts.size() == 0 && !subclasses_done) {
|
|
|
contexts << "Qt";
|
|
|
subclasses_done = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!type) {
|
|
|
*ok = false;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// Used to for diagnostics later...
|
|
|
m_used_types << type;
|
|
|
|
|
|
// These are only implicit and should not appear in code...
|
|
|
Q_ASSERT(!type->isInterface());
|
|
|
|
|
|
AbstractMetaType *meta_type = createMetaType();
|
|
|
meta_type->setTypeEntry(type);
|
|
|
meta_type->setIndirections(typeInfo.indirections);
|
|
|
meta_type->setReference(typeInfo.is_reference);
|
|
|
meta_type->setConstant(typeInfo.is_constant);
|
|
|
meta_type->setOriginalTypeDescription(_typei.toString());
|
|
|
decideUsagePattern(meta_type);
|
|
|
|
|
|
if (meta_type->typeEntry()->isContainer()) {
|
|
|
ContainerTypeEntry::Type container_type = static_cast<const ContainerTypeEntry *>(type)->type();
|
|
|
|
|
|
if (container_type == ContainerTypeEntry::StringListContainer) {
|
|
|
TypeInfo info;
|
|
|
info.setQualifiedName(QStringList() << "QString");
|
|
|
AbstractMetaType *targ_type = translateType(info, ok);
|
|
|
|
|
|
Q_ASSERT(*ok);
|
|
|
Q_ASSERT(targ_type);
|
|
|
|
|
|
meta_type->addInstantiation(targ_type);
|
|
|
meta_type->setInstantiationInCpp(false);
|
|
|
|
|
|
} else {
|
|
|
foreach (const TypeParser::Info &ta, typeInfo.template_instantiations) {
|
|
|
TypeInfo info;
|
|
|
info.setConstant(ta.is_constant);
|
|
|
info.setReference(ta.is_reference);
|
|
|
info.setIndirections(ta.indirections);
|
|
|
|
|
|
info.setFunctionPointer(false);
|
|
|
info.setQualifiedName(ta.instantiationName().split("::"));
|
|
|
|
|
|
AbstractMetaType *targ_type = translateType(info, ok);
|
|
|
if (!(*ok)) {
|
|
|
delete meta_type;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
meta_type->addInstantiation(targ_type);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (container_type == ContainerTypeEntry::ListContainer
|
|
|
|| container_type == ContainerTypeEntry::VectorContainer
|
|
|
|| container_type == ContainerTypeEntry::StringListContainer) {
|
|
|
Q_ASSERT(meta_type->instantiations().size() == 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return meta_type;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::decideUsagePattern(AbstractMetaType *meta_type)
|
|
|
{
|
|
|
const TypeEntry *type = meta_type->typeEntry();
|
|
|
|
|
|
if (type->isPrimitive() && (meta_type->actualIndirections() == 0
|
|
|
|| (meta_type->isConstant() && meta_type->isReference() && meta_type->indirections() == 0))) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::PrimitivePattern);
|
|
|
|
|
|
} else if (type->isVoid()) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern);
|
|
|
|
|
|
} else if (type->isString()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& (meta_type->isConstant() == meta_type->isReference()
|
|
|
|| meta_type->isConstant())) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::StringPattern);
|
|
|
|
|
|
} else if (type->isChar()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& meta_type->isConstant() == meta_type->isReference()) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::CharPattern);
|
|
|
|
|
|
} else if (type->isJObjectWrapper()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& meta_type->isConstant() == meta_type->isReference()) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::JObjectWrapperPattern);
|
|
|
|
|
|
} else if (type->isVariant()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& meta_type->isConstant() == meta_type->isReference()) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::VariantPattern);
|
|
|
|
|
|
} else if (type->isEnum() && meta_type->actualIndirections() == 0) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::EnumPattern);
|
|
|
|
|
|
} else if (type->isObject()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& meta_type->isReference()) {
|
|
|
if (((ComplexTypeEntry *) type)->isQObject())
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern);
|
|
|
else
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern);
|
|
|
|
|
|
} else if (type->isObject()
|
|
|
&& meta_type->indirections() == 1) {
|
|
|
if (((ComplexTypeEntry *) type)->isQObject())
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern);
|
|
|
else
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern);
|
|
|
|
|
|
// const-references to pointers can be passed as pointers
|
|
|
if (meta_type->isReference() && meta_type->isConstant()) {
|
|
|
meta_type->setReference(false);
|
|
|
meta_type->setConstant(false);
|
|
|
}
|
|
|
|
|
|
} else if (type->isContainer() && meta_type->indirections() == 0) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ContainerPattern);
|
|
|
|
|
|
} else if (type->isTemplateArgument()) {
|
|
|
|
|
|
} else if (type->isFlags()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& (meta_type->isConstant() == meta_type->isReference())) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::FlagsPattern);
|
|
|
|
|
|
} else if (type->isArray()) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ArrayPattern);
|
|
|
|
|
|
} else if (type->isThread()) {
|
|
|
Q_ASSERT(meta_type->indirections() == 1);
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ThreadPattern);
|
|
|
|
|
|
} else if (type->isValue()
|
|
|
&& meta_type->indirections() == 0
|
|
|
&& (meta_type->isConstant() == meta_type->isReference()
|
|
|
|| !meta_type->isReference())) {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::ValuePattern);
|
|
|
|
|
|
} else {
|
|
|
meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern);
|
|
|
ReportHandler::debugFull(QString("native pointer pattern for '%1'")
|
|
|
.arg(meta_type->cppSignature()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QString AbstractMetaBuilder::translateDefaultValue(ArgumentModelItem item, AbstractMetaType *type,
|
|
|
AbstractMetaFunction *fnc, AbstractMetaClass *implementing_class,
|
|
|
int argument_index)
|
|
|
{
|
|
|
QString function_name = fnc->name();
|
|
|
QString class_name = implementing_class->name();
|
|
|
|
|
|
QString replaced_expression = fnc->replacedDefaultExpression(implementing_class, argument_index + 1);
|
|
|
if (fnc->removedDefaultExpression(implementing_class, argument_index +1))
|
|
|
return "";
|
|
|
if (!replaced_expression.isEmpty())
|
|
|
return replaced_expression;
|
|
|
|
|
|
QString expr = item->defaultValueExpression();
|
|
|
if (type != 0 && type->isPrimitive()) {
|
|
|
if (type->name() == "boolean") {
|
|
|
if (expr == "false" || expr=="true") {
|
|
|
return expr;
|
|
|
} else {
|
|
|
bool ok = false;
|
|
|
int number = expr.toInt(&ok);
|
|
|
if (ok && number)
|
|
|
return "true";
|
|
|
else
|
|
|
return "false";
|
|
|
}
|
|
|
} else if (expr == "ULONG_MAX") {
|
|
|
return "Long.MAX_VALUE";
|
|
|
} else if (expr == "QVariant::Invalid") {
|
|
|
return QString::number(QVariant::Invalid);
|
|
|
} else {
|
|
|
// This can be an enum or flag so I need to delay the
|
|
|
// translation untill all namespaces are completly
|
|
|
// processed. This is done in figureOutEnumValues()
|
|
|
return expr;
|
|
|
}
|
|
|
} else if (type != 0 && (type->isFlags() || type->isEnum())) {
|
|
|
// Same as with enum explanation above...
|
|
|
return expr;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// constructor or functioncall can be a bit tricky...
|
|
|
if (expr == "QVariant()" || expr == "QModelIndex()") {
|
|
|
return "null";
|
|
|
} else if (expr == "QString()") {
|
|
|
return "null";
|
|
|
} else if (expr.endsWith(")") && expr.contains("::")) {
|
|
|
TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::")));
|
|
|
if (typeEntry)
|
|
|
return typeEntry->qualifiedTargetLangName() + "." + expr.right(expr.length() - expr.indexOf("::") - 2);
|
|
|
} else if (expr.endsWith(")") && type != 0 && type->isValue()) {
|
|
|
int pos = expr.indexOf("(");
|
|
|
|
|
|
TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(pos));
|
|
|
if (typeEntry)
|
|
|
return "new " + typeEntry->qualifiedTargetLangName() + expr.right(expr.length() - pos);
|
|
|
else
|
|
|
return expr;
|
|
|
} else if (expr == "0") {
|
|
|
return "null";
|
|
|
} else if (type != 0 && (type->isObject() || type->isValue() || expr.contains("::"))) { // like Qt::black passed to a QColor
|
|
|
TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::")));
|
|
|
|
|
|
expr = expr.right(expr.length() - expr.indexOf("::") - 2);
|
|
|
if (typeEntry) {
|
|
|
return "new " + type->typeEntry()->qualifiedTargetLangName() +
|
|
|
"(" + typeEntry->qualifiedTargetLangName() + "." + expr + ")";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QString warn = QString("unsupported default value '%3' of argument in function '%1', class '%2'")
|
|
|
.arg(function_name).arg(class_name).arg(item->defaultValueExpression());
|
|
|
ReportHandler::warning(warn);
|
|
|
|
|
|
return QString();
|
|
|
}
|
|
|
|
|
|
|
|
|
bool AbstractMetaBuilder::isQObject(const QString &qualified_name)
|
|
|
{
|
|
|
if (qualified_name == "QObject")
|
|
|
return true;
|
|
|
|
|
|
ClassModelItem class_item = m_dom->findClass(qualified_name);
|
|
|
|
|
|
if (!class_item) {
|
|
|
QStringList names = qualified_name.split(QLatin1String("::"));
|
|
|
NamespaceModelItem ns = model_dynamic_cast<NamespaceModelItem>(m_dom);
|
|
|
for (int i=0; i<names.size() - 1 && ns; ++i)
|
|
|
ns = ns->namespaceMap().value(names.at(i));
|
|
|
if (ns && names.size() >= 2)
|
|
|
class_item = ns->findClass(names.at(names.size() - 1));
|
|
|
}
|
|
|
|
|
|
bool isqobject = class_item && class_item->extendsClass("QObject");
|
|
|
|
|
|
if (class_item && !isqobject) {
|
|
|
QStringList baseClasses = class_item->baseClasses();
|
|
|
for (int i=0; i<baseClasses.count(); ++i) {
|
|
|
|
|
|
isqobject = isQObject(baseClasses.at(i));
|
|
|
if (isqobject)
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return isqobject;
|
|
|
}
|
|
|
|
|
|
|
|
|
bool AbstractMetaBuilder::isEnum(const QStringList &qualified_name)
|
|
|
{
|
|
|
CodeModelItem item = m_dom->model()->findItem(qualified_name, m_dom->toItem());
|
|
|
return item && item->kind() == _EnumModelItem::__node_kind;
|
|
|
}
|
|
|
|
|
|
AbstractMetaType *AbstractMetaBuilder::inheritTemplateType(const QList<AbstractMetaType *> &template_types,
|
|
|
AbstractMetaType *meta_type, bool *ok)
|
|
|
{
|
|
|
if (ok != 0)
|
|
|
*ok = true;
|
|
|
if (!meta_type || (!meta_type->typeEntry()->isTemplateArgument() && !meta_type->hasInstantiations()))
|
|
|
return meta_type ? meta_type->copy() : 0;
|
|
|
|
|
|
AbstractMetaType *returned = meta_type->copy();
|
|
|
returned->setOriginalTemplateType(meta_type->copy());
|
|
|
|
|
|
if (returned->typeEntry()->isTemplateArgument()) {
|
|
|
const TemplateArgumentEntry *tae = static_cast<const TemplateArgumentEntry *>(returned->typeEntry());
|
|
|
|
|
|
// If the template is intantiated with void we special case this as rejecting the functions that use this
|
|
|
// parameter from the instantiation.
|
|
|
if (template_types.size() <= tae->ordinal() || template_types.at(tae->ordinal())->typeEntry()->name() == "void") {
|
|
|
if (ok != 0)
|
|
|
*ok = false;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
AbstractMetaType *t = returned->copy();
|
|
|
t->setTypeEntry(template_types.at(tae->ordinal())->typeEntry());
|
|
|
t->setIndirections(template_types.at(tae->ordinal())->indirections() + t->indirections()
|
|
|
? 1
|
|
|
: 0);
|
|
|
decideUsagePattern(t);
|
|
|
|
|
|
delete returned;
|
|
|
returned = inheritTemplateType(template_types, t, ok);
|
|
|
if (ok != 0 && !(*ok))
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (returned->hasInstantiations()) {
|
|
|
QList<AbstractMetaType *> instantiations = returned->instantiations();
|
|
|
for (int i=0; i<instantiations.count(); ++i) {
|
|
|
instantiations[i] = inheritTemplateType(template_types, instantiations.at(i), ok);
|
|
|
if (ok != 0 && !(*ok))
|
|
|
return 0;
|
|
|
}
|
|
|
returned->setInstantiations(instantiations);
|
|
|
}
|
|
|
|
|
|
return returned;
|
|
|
}
|
|
|
|
|
|
bool AbstractMetaBuilder::inheritTemplate(AbstractMetaClass *subclass,
|
|
|
const AbstractMetaClass *template_class,
|
|
|
const TypeParser::Info &info)
|
|
|
{
|
|
|
QList<TypeParser::Info> targs = info.template_instantiations;
|
|
|
|
|
|
QList<AbstractMetaType *> template_types;
|
|
|
foreach (const TypeParser::Info &i, targs) {
|
|
|
TypeEntry *t = TypeDatabase::instance()->findType(i.qualified_name.join("::"));
|
|
|
|
|
|
if (t != 0) {
|
|
|
AbstractMetaType *temporary_type = createMetaType();
|
|
|
temporary_type->setTypeEntry(t);
|
|
|
temporary_type->setConstant(i.is_constant);
|
|
|
temporary_type->setReference(i.is_reference);
|
|
|
temporary_type->setIndirections(i.indirections);
|
|
|
template_types << temporary_type;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AbstractMetaFunctionList funcs = subclass->functions();
|
|
|
foreach (const AbstractMetaFunction *function, template_class->functions()) {
|
|
|
|
|
|
if (function->isModifiedRemoved(TypeSystem::All))
|
|
|
continue;
|
|
|
|
|
|
AbstractMetaFunction *f = function->copy();
|
|
|
f->setArguments(AbstractMetaArgumentList());
|
|
|
|
|
|
bool ok = true;
|
|
|
AbstractMetaType *ftype = function->type();
|
|
|
f->setType(inheritTemplateType(template_types, ftype, &ok));
|
|
|
if (!ok) {
|
|
|
delete f;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
foreach (AbstractMetaArgument *argument, function->arguments()) {
|
|
|
AbstractMetaType *atype = argument->type();
|
|
|
|
|
|
AbstractMetaArgument *arg = argument->copy();
|
|
|
arg->setType(inheritTemplateType(template_types, atype, &ok));
|
|
|
if (!ok)
|
|
|
break;
|
|
|
f->addArgument(arg);
|
|
|
}
|
|
|
|
|
|
if (!ok) {
|
|
|
delete f;
|
|
|
continue ;
|
|
|
}
|
|
|
|
|
|
// There is no base class in java to inherit from here, so the
|
|
|
// template instantiation is the class that implements the function..
|
|
|
f->setImplementingClass(subclass);
|
|
|
|
|
|
// We also set it as the declaring class, since the superclass is
|
|
|
// supposed to disappear. This allows us to make certain function modifications
|
|
|
// on the inherited functions.
|
|
|
f->setDeclaringClass(subclass);
|
|
|
|
|
|
|
|
|
if (f->isConstructor() && subclass->isTypeAlias()) {
|
|
|
f->setName(subclass->name());
|
|
|
} else if (f->isConstructor()) {
|
|
|
delete f;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// if the instantiation has a function named the same as an existing
|
|
|
// function we have shadowing so we need to skip it.
|
|
|
bool found = false;
|
|
|
for (int i=0; i<funcs.size(); ++i) {
|
|
|
if (funcs.at(i)->name() == f->name()) {
|
|
|
found = true;
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
if (found) {
|
|
|
delete f;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
ComplexTypeEntry *te = subclass->typeEntry();
|
|
|
FunctionModificationList mods = function->modifications(template_class);
|
|
|
for (int i=0; i<mods.size(); ++i) {
|
|
|
FunctionModification mod = mods.at(i);
|
|
|
mod.signature = f->minimalSignature();
|
|
|
|
|
|
// If we ever need it... Below is the code to do
|
|
|
// substitution of the template instantation type inside
|
|
|
// injected code..
|
|
|
#if 0
|
|
|
if (mod.modifiers & Modification::CodeInjection) {
|
|
|
for (int j=0; j<template_types.size(); ++j) {
|
|
|
CodeSnip &snip = mod.snips.last();
|
|
|
QString code = snip.code();
|
|
|
code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j),
|
|
|
template_types.at(j)->typeEntry()->qualifiedCppName());
|
|
|
snip.codeList.clear();
|
|
|
snip.addCode(code);
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
te->addFunctionModification(mod);
|
|
|
}
|
|
|
|
|
|
subclass->addFunction(f);
|
|
|
}
|
|
|
|
|
|
// Clean up
|
|
|
foreach (AbstractMetaType *type, template_types) {
|
|
|
delete type;
|
|
|
}
|
|
|
|
|
|
|
|
|
{
|
|
|
subclass->setTemplateBaseClass(template_class);
|
|
|
|
|
|
subclass->setInterfaces(template_class->interfaces());
|
|
|
subclass->setBaseClass(template_class->baseClass());
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::parseQ_Property(AbstractMetaClass *meta_class, const QStringList &declarations)
|
|
|
{
|
|
|
for (int i=0; i<declarations.size(); ++i) {
|
|
|
QString p = declarations.at(i);
|
|
|
|
|
|
QStringList l = p.split(QLatin1String(" "));
|
|
|
|
|
|
|
|
|
QStringList qualifiedScopeName = currentScope()->qualifiedName();
|
|
|
bool ok = false;
|
|
|
AbstractMetaType *type = 0;
|
|
|
QString scope;
|
|
|
for (int j=qualifiedScopeName.size(); j>=0; --j) {
|
|
|
scope = j > 0 ? QStringList(qualifiedScopeName.mid(0, j)).join("::") + "::" : QString();
|
|
|
TypeInfo info;
|
|
|
info.setQualifiedName((scope + l.at(0)).split("::"));
|
|
|
|
|
|
type = translateType(info, &ok);
|
|
|
if (type != 0 && ok) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (type == 0 || !ok) {
|
|
|
ReportHandler::warning(QString("Unable to decide type of property: '%1' in class '%2'")
|
|
|
.arg(l.at(0)).arg(meta_class->name()));
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QString typeName = scope + l.at(0);
|
|
|
|
|
|
QPropertySpec *spec = new QPropertySpec(type->typeEntry());
|
|
|
spec->setName(l.at(1));
|
|
|
spec->setIndex(i);
|
|
|
|
|
|
for (int pos=2; pos+1<l.size(); pos+=2) {
|
|
|
if (l.at(pos) == QLatin1String("READ"))
|
|
|
spec->setRead(l.at(pos+1));
|
|
|
else if (l.at(pos) == QLatin1String("WRITE"))
|
|
|
spec->setWrite(l.at(pos+1));
|
|
|
else if (l.at(pos) == QLatin1String("DESIGNABLE"))
|
|
|
spec->setDesignable(l.at(pos+1));
|
|
|
else if (l.at(pos) == QLatin1String("RESET"))
|
|
|
spec->setReset(l.at(pos+1));
|
|
|
}
|
|
|
|
|
|
meta_class->addPropertySpec(spec);
|
|
|
delete type;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void hide_functions(const AbstractMetaFunctionList &l) {
|
|
|
foreach (AbstractMetaFunction *f, l) {
|
|
|
FunctionModification mod;
|
|
|
mod.signature = f->minimalSignature();
|
|
|
mod.modifiers = FunctionModification::Private;
|
|
|
((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void remove_function(AbstractMetaFunction *f) {
|
|
|
FunctionModification mod;
|
|
|
mod.removal = TypeSystem::All;
|
|
|
mod.signature = f->minimalSignature();
|
|
|
((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod);
|
|
|
}
|
|
|
|
|
|
static AbstractMetaFunctionList filter_functions(const AbstractMetaFunctionList &lst, QSet<QString> *signatures)
|
|
|
{
|
|
|
AbstractMetaFunctionList functions;
|
|
|
foreach (AbstractMetaFunction *f, lst) {
|
|
|
QString signature = f->minimalSignature();
|
|
|
int start = signature.indexOf(QLatin1Char('(')) + 1;
|
|
|
int end = signature.lastIndexOf(QLatin1Char(')'));
|
|
|
signature = signature.mid(start, end - start);
|
|
|
if (signatures->contains(signature)) {
|
|
|
remove_function(f);
|
|
|
continue;
|
|
|
}
|
|
|
(*signatures) << signature;
|
|
|
functions << f;
|
|
|
}
|
|
|
return functions;
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::setupEquals(AbstractMetaClass *cls)
|
|
|
{
|
|
|
AbstractMetaFunctionList equals;
|
|
|
AbstractMetaFunctionList nequals;
|
|
|
|
|
|
QString op_equals = QLatin1String("operator_equal");
|
|
|
QString op_nequals = QLatin1String("operator_not_equal");
|
|
|
|
|
|
AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements
|
|
|
| AbstractMetaClass::NotRemovedFromTargetLang);
|
|
|
foreach (AbstractMetaFunction *f, functions) {
|
|
|
if (f->name() == op_equals)
|
|
|
equals << f;
|
|
|
else if (f->name() == op_nequals)
|
|
|
nequals << f;
|
|
|
}
|
|
|
|
|
|
if (equals.size() || nequals.size()) {
|
|
|
if (!cls->hasHashFunction()) {
|
|
|
ReportHandler::warning(QString::fromLatin1("Class '%1' has equals operators but no qHash() function")
|
|
|
.arg(cls->name()));
|
|
|
}
|
|
|
|
|
|
hide_functions(equals);
|
|
|
hide_functions(nequals);
|
|
|
|
|
|
// We only need == if we have both == and !=, and one == for
|
|
|
// each signature type, like QDateTime::==(QDate) and (QTime)
|
|
|
// if such a thing exists...
|
|
|
QSet<QString> func_signatures;
|
|
|
cls->setEqualsFunctions(filter_functions(equals, &func_signatures));
|
|
|
cls->setNotEqualsFunctions(filter_functions(nequals, &func_signatures));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::setupComparable(AbstractMetaClass *cls)
|
|
|
{
|
|
|
AbstractMetaFunctionList greater;
|
|
|
AbstractMetaFunctionList greaterEquals;
|
|
|
AbstractMetaFunctionList less;
|
|
|
AbstractMetaFunctionList lessEquals;
|
|
|
|
|
|
QString op_greater = QLatin1String("operator_greater");
|
|
|
QString op_greater_eq = QLatin1String("operator_greater_or_equal");
|
|
|
QString op_less = QLatin1String("operator_less");
|
|
|
QString op_less_eq = QLatin1String("operator_less_or_equal");
|
|
|
|
|
|
AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements
|
|
|
| AbstractMetaClass::NotRemovedFromTargetLang);
|
|
|
foreach (AbstractMetaFunction *f, functions) {
|
|
|
if (f->name() == op_greater)
|
|
|
greater << f;
|
|
|
else if (f->name() == op_greater_eq)
|
|
|
greaterEquals << f;
|
|
|
else if (f->name() == op_less)
|
|
|
less << f;
|
|
|
else if (f->name() == op_less_eq)
|
|
|
lessEquals << f;
|
|
|
}
|
|
|
|
|
|
bool hasEquals = cls->equalsFunctions().size() || cls->notEqualsFunctions().size();
|
|
|
|
|
|
// Conditions for comparable is:
|
|
|
// >, ==, < - The basic case
|
|
|
// >, == - Less than becomes else case
|
|
|
// <, == - Greater than becomes else case
|
|
|
// >=, <= - if (<= && >=) -> equal
|
|
|
bool mightBeComparable = greater.size() || greaterEquals.size() || less.size() || lessEquals.size()
|
|
|
|| greaterEquals.size() == 1 || lessEquals.size() == 1;
|
|
|
|
|
|
if (mightBeComparable) {
|
|
|
QSet<QString> signatures;
|
|
|
|
|
|
// We only hide the original functions if we are able to make a compareTo() method
|
|
|
bool wasComparable = false;
|
|
|
|
|
|
// The three upper cases, prefer the <, == approach
|
|
|
if (hasEquals && (greater.size() || less.size())) {
|
|
|
cls->setLessThanFunctions(filter_functions(less, &signatures));
|
|
|
cls->setGreaterThanFunctions(filter_functions(greater, &signatures));
|
|
|
filter_functions(greaterEquals, &signatures);
|
|
|
filter_functions(lessEquals, &signatures);
|
|
|
wasComparable = true;
|
|
|
} else if (hasEquals && (greaterEquals.size() || lessEquals.size())) {
|
|
|
cls->setLessThanEqFunctions(filter_functions(lessEquals, &signatures));
|
|
|
cls->setGreaterThanEqFunctions(filter_functions(greaterEquals, &signatures));
|
|
|
wasComparable = true;
|
|
|
} else if (greaterEquals.size() == 1 || lessEquals.size() == 1) {
|
|
|
cls->setGreaterThanEqFunctions(greaterEquals);
|
|
|
cls->setLessThanEqFunctions(lessEquals);
|
|
|
filter_functions(less, &signatures);
|
|
|
filter_functions(greater, &signatures);
|
|
|
wasComparable = true;
|
|
|
}
|
|
|
|
|
|
if (wasComparable) {
|
|
|
hide_functions(greater);
|
|
|
hide_functions(greaterEquals);
|
|
|
hide_functions(less);
|
|
|
hide_functions(lessEquals);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void AbstractMetaBuilder::setupClonable(AbstractMetaClass *cls)
|
|
|
{
|
|
|
QString op_assign = QLatin1String("operator_assign");
|
|
|
|
|
|
AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements);
|
|
|
foreach (AbstractMetaFunction *f, functions) {
|
|
|
if ((f->name() == op_assign || f->isConstructor()) && f->isPublic()) {
|
|
|
AbstractMetaArgumentList arguments = f->arguments();
|
|
|
if (arguments.size() == 1) {
|
|
|
if (cls->typeEntry()->qualifiedCppName() == arguments.at(0)->type()->typeEntry()->qualifiedCppName()) {
|
|
|
if (cls->typeEntry()->isValue()) {
|
|
|
cls->setHasCloneOperator(true);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void write_reject_log_file(const QString &name,
|
|
|
const QMap<QString, AbstractMetaBuilder::RejectReason> &rejects)
|
|
|
{
|
|
|
QFile f(name);
|
|
|
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
|
ReportHandler::warning(QString("failed to write log file: '%1'")
|
|
|
.arg(f.fileName()));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QTextStream s(&f);
|
|
|
|
|
|
|
|
|
for (int reason=0; reason<AbstractMetaBuilder::NoReason; ++reason) {
|
|
|
s << QString(72, '*') << endl;
|
|
|
switch (reason) {
|
|
|
case AbstractMetaBuilder::NotInTypeSystem:
|
|
|
s << "Not in type system";
|
|
|
break;
|
|
|
case AbstractMetaBuilder::GenerationDisabled:
|
|
|
s << "Generation disabled by type system";
|
|
|
break;
|
|
|
case AbstractMetaBuilder::RedefinedToNotClass:
|
|
|
s << "Type redefined to not be a class";
|
|
|
break;
|
|
|
|
|
|
case AbstractMetaBuilder::UnmatchedReturnType:
|
|
|
s << "Unmatched return type";
|
|
|
break;
|
|
|
|
|
|
case AbstractMetaBuilder::UnmatchedArgumentType:
|
|
|
s << "Unmatched argument type";
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
s << "unknown reason";
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
s << endl;
|
|
|
|
|
|
for (QMap<QString, AbstractMetaBuilder::RejectReason>::const_iterator it = rejects.constBegin();
|
|
|
it != rejects.constEnd(); ++it) {
|
|
|
if (it.value() != reason)
|
|
|
continue;
|
|
|
s << " - " << it.key() << endl;
|
|
|
}
|
|
|
|
|
|
s << QString(72, '*') << endl << endl;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
void AbstractMetaBuilder::dumpLog()
|
|
|
{
|
|
|
write_reject_log_file("mjb_rejected_classes.log", m_rejected_classes);
|
|
|
write_reject_log_file("mjb_rejected_enums.log", m_rejected_enums);
|
|
|
write_reject_log_file("mjb_rejected_functions.log", m_rejected_functions);
|
|
|
write_reject_log_file("mjb_rejected_fields.log", m_rejected_fields);
|
|
|
}
|
|
|
|
|
|
AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted() const
|
|
|
{
|
|
|
AbstractMetaClassList res;
|
|
|
|
|
|
AbstractMetaClassList classes = m_meta_classes;
|
|
|
qSort(classes);
|
|
|
|
|
|
QSet<AbstractMetaClass*> noDependency;
|
|
|
QHash<AbstractMetaClass*, QSet<AbstractMetaClass* >* > hash;
|
|
|
foreach (AbstractMetaClass *cls, classes) {
|
|
|
QSet<AbstractMetaClass* > *depends = new QSet<AbstractMetaClass* >();
|
|
|
|
|
|
if (cls->baseClass())
|
|
|
depends->insert(cls->baseClass());
|
|
|
|
|
|
foreach (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()) {
|
|
|
foreach (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.");
|
|
|
}
|
|
|
|
|
|
return res;
|
|
|
}
|
|
|
|