abstractmetabuilder.cpp
2634 lines
| 95.1 KiB
| text/x-c
|
CppLexer
/ generator / abstractmetabuilder.cpp
florianlink
|
r10 | /**************************************************************************** | ||
** | ||||
florianlink
|
r87 | ** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). | ||
** All rights reserved. | ||||
** Contact: Nokia Corporation (qt-info@nokia.com) | ||||
florianlink
|
r10 | ** | ||
florianlink
|
r87 | ** This file is part of the Qt Script Generator project on Qt Labs. | ||
florianlink
|
r10 | ** | ||
florianlink
|
r87 | ** $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. | ||||
florianlink
|
r10 | ** | ||
florianlink
|
r87 | ** 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. | ||||
florianlink
|
r10 | ** | ||
florianlink
|
r87 | ** 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$ | ||||
florianlink
|
r10 | ** | ||
****************************************************************************/ | ||||
#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>; | ||||
florianlink
|
r76 | 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("~", "__negate__"); | ||||
operator_names->insert("<<", "__lshift__"); | ||||
operator_names->insert(">>", "__rshift__"); | ||||
florianlink
|
r10 | |||
// assigments | ||||
operator_names->insert("=", "assign"); | ||||
florianlink
|
r76 | 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__"); | ||||
florianlink
|
r10 | |||
// 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("<", "less"); | ||||
operator_names->insert(">", "greater"); | ||||
operator_names->insert("<=", "less_or_equal"); | ||||
operator_names->insert(">=", "greater_or_equal"); | ||||
operator_names->insert("!=", "not_equal"); | ||||
operator_names->insert("==", "equal"); | ||||
// 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; | ||||
} | ||||
} | ||||
florianlink
|
r76 | QString r = operator_names->value(op); | ||
if (r.startsWith("__")) { | ||||
return r; | ||||
} else { | ||||
return "operator_" + r; | ||||
} | ||||
florianlink
|
r10 | } | ||
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->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; | ||||
} | ||||
} | ||||
} | ||||
} | ||||
florianlink
|
r74 | 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; | ||||
} | ||||
} | ||||
} | ||||
florianlink
|
r10 | 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); | ||||
florianlink
|
r87 | |||
// Global enums should be public despite not having public | ||||
// identifiers so we'll fix the original attributes here. | ||||
meta_enum->setOriginalAttributes(meta_enum->attributes()); | ||||
florianlink
|
r10 | } | ||
} | ||||
// 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); | ||||
} | ||||
} | ||||
florianlink
|
r74 | { | ||
florianlink
|
r75 | 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^"); | ||||
florianlink
|
r74 | foreach (FunctionModelItem item, stream_operators) { | ||
traverseBinaryArithmeticOperator(item); | ||||
} | ||||
} | ||||
florianlink
|
r10 | { | ||
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; | ||||
} | ||||
florianlink
|
r87 | QFileInfo info(enum_item->fileName()); | ||
meta_enum->typeEntry()->setInclude(Include(Include::IncludePath, info.fileName())); | ||||
florianlink
|
r10 | 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; | ||||
} | ||||