/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the tools applications of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "generator.h" | |
#include "outputrevision.h" | |
#include "utils.h" | |
#include <QtCore/qmetatype.h> | |
#include <stdio.h> | |
#include <private/qmetaobject_p.h> //for the flags. | |
QT_BEGIN_NAMESPACE | |
uint qvariant_nameToType(const char* name) | |
{ | |
if (!name) | |
return 0; | |
if (strcmp(name, "QVariant") == 0) | |
return 0xffffffff; | |
if (strcmp(name, "QCString") == 0) | |
return QMetaType::QByteArray; | |
if (strcmp(name, "Q_LLONG") == 0) | |
return QMetaType::LongLong; | |
if (strcmp(name, "Q_ULLONG") == 0) | |
return QMetaType::ULongLong; | |
if (strcmp(name, "QIconSet") == 0) | |
return QMetaType::QIcon; | |
uint tp = QMetaType::type(name); | |
return tp < QMetaType::User ? tp : 0; | |
} | |
/* | |
Returns true if the type is a QVariant types. | |
*/ | |
bool isVariantType(const char* type) | |
{ | |
return qvariant_nameToType(type) != 0; | |
} | |
/*! | |
Returns true if the type is qreal. | |
*/ | |
static bool isQRealType(const char *type) | |
{ | |
return strcmp(type, "qreal") == 0; | |
} | |
Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, FILE *outfile) | |
: out(outfile), cdef(classDef), metaTypes(metaTypes) | |
{ | |
if (cdef->superclassList.size()) | |
purestSuperClass = cdef->superclassList.first().first; | |
} | |
static inline int lengthOfEscapeSequence(const QByteArray &s, int i) | |
{ | |
if (s.at(i) != '\\' || i >= s.length() - 1) | |
return 1; | |
const int startPos = i; | |
++i; | |
char ch = s.at(i); | |
if (ch == 'x') { | |
++i; | |
while (i < s.length() && is_hex_char(s.at(i))) | |
++i; | |
} else if (is_octal_char(ch)) { | |
while (i < startPos + 4 | |
&& i < s.length() | |
&& is_octal_char(s.at(i))) { | |
++i; | |
} | |
} else { // single character escape sequence | |
i = qMin(i + 1, s.length()); | |
} | |
return i - startPos; | |
} | |
int Generator::strreg(const char *s) | |
{ | |
int idx = 0; | |
if (!s) | |
s = ""; | |
for (int i = 0; i < strings.size(); ++i) { | |
const QByteArray &str = strings.at(i); | |
if (str == s) | |
return idx; | |
idx += str.length() + 1; | |
for (int i = 0; i < str.length(); ++i) { | |
if (str.at(i) == '\\') { | |
int cnt = lengthOfEscapeSequence(str, i) - 1; | |
idx -= cnt; | |
i += cnt; | |
} | |
} | |
} | |
strings.append(s); | |
return idx; | |
} | |
void Generator::generateCode() | |
{ | |
bool isQt = (cdef->classname == "Qt"); | |
bool isQObject = (cdef->classname == "QObject"); | |
bool isConstructible = !cdef->constructorList.isEmpty(); | |
// | |
// build the data array | |
// | |
int i = 0; | |
// filter out undeclared enumerators and sets | |
{ | |
QList<EnumDef> enumList; | |
for (i = 0; i < cdef->enumList.count(); ++i) { | |
EnumDef def = cdef->enumList.at(i); | |
if (cdef->enumDeclarations.contains(def.name)) { | |
enumList += def; | |
} | |
QByteArray alias = cdef->flagAliases.value(def.name); | |
if (cdef->enumDeclarations.contains(alias)) { | |
def.name = alias; | |
enumList += def; | |
} | |
} | |
cdef->enumList = enumList; | |
} | |
QByteArray qualifiedClassNameIdentifier = cdef->qualified; | |
qualifiedClassNameIdentifier.replace(':', '_'); | |
int index = 14; | |
fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); | |
fprintf(out, "\n // content:\n"); | |
fprintf(out, " %4d, // revision\n", 5); | |
fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); | |
fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); | |
index += cdef->classInfoList.count() * 2; | |
int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count(); | |
fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0); | |
index += methodCount * 5; | |
if (cdef->revisionedMethods) | |
index += methodCount; | |
fprintf(out, " %4d, %4d, // properties\n", cdef->propertyList.count(), cdef->propertyList.count() ? index : 0); | |
index += cdef->propertyList.count() * 3; | |
if(cdef->notifyableProperties) | |
index += cdef->propertyList.count(); | |
if (cdef->revisionedProperties) | |
index += cdef->propertyList.count(); | |
fprintf(out, " %4d, %4d, // enums/sets\n", cdef->enumList.count(), cdef->enumList.count() ? index : 0); | |
int enumsIndex = index; | |
for (i = 0; i < cdef->enumList.count(); ++i) | |
index += 4 + (cdef->enumList.at(i).values.count() * 2); | |
fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? cdef->constructorList.count() : 0, | |
isConstructible ? index : 0); | |
fprintf(out, " %4d, // flags\n", 0); | |
fprintf(out, " %4d, // signalCount\n", cdef->signalList.count()); | |
// | |
// Build classinfo array | |
// | |
generateClassInfos(); | |
// | |
// Build signals array first, otherwise the signal indices would be wrong | |
// | |
generateFunctions(cdef->signalList, "signal", MethodSignal); | |
// | |
// Build slots array | |
// | |
generateFunctions(cdef->slotList, "slot", MethodSlot); | |
// | |
// Build method array | |
// | |
generateFunctions(cdef->methodList, "method", MethodMethod); | |
// | |
// Build method version arrays | |
// | |
if (cdef->revisionedMethods) { | |
generateFunctionRevisions(cdef->signalList, "signal"); | |
generateFunctionRevisions(cdef->slotList, "slot"); | |
generateFunctionRevisions(cdef->methodList, "method"); | |
} | |
// | |
// Build property array | |
// | |
generateProperties(); | |
// | |
// Build enums array | |
// | |
generateEnums(enumsIndex); | |
// | |
// Build constructors array | |
// | |
if (isConstructible) | |
generateFunctions(cdef->constructorList, "constructor", MethodConstructor); | |
// | |
// Terminate data array | |
// | |
fprintf(out, "\n 0 // eod\n};\n\n"); | |
// | |
// Build stringdata array | |
// | |
fprintf(out, "static const char qt_meta_stringdata_%s[] = {\n", qualifiedClassNameIdentifier.constData()); | |
fprintf(out, " \""); | |
int col = 0; | |
int len = 0; | |
for (i = 0; i < strings.size(); ++i) { | |
QByteArray s = strings.at(i); | |
len = s.length(); | |
if (col && col + len >= 72) { | |
fprintf(out, "\"\n \""); | |
col = 0; | |
} else if (len && s.at(0) >= '0' && s.at(0) <= '9') { | |
fprintf(out, "\"\""); | |
len += 2; | |
} | |
int idx = 0; | |
while (idx < s.length()) { | |
if (idx > 0) { | |
col = 0; | |
fprintf(out, "\"\n \""); | |
} | |
int spanLen = qMin(70, s.length() - idx); | |
// don't cut escape sequences at the end of a line | |
int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); | |
if (backSlashPos >= idx) { | |
int escapeLen = lengthOfEscapeSequence(s, backSlashPos); | |
spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); | |
} | |
fwrite(s.constData() + idx, 1, spanLen, out); | |
idx += spanLen; | |
col += spanLen; | |
} | |
fputs("\\0", out); | |
col += len + 2; | |
} | |
fprintf(out, "\"\n};\n\n"); | |
// | |
// Generate internal qt_static_metacall() function | |
// | |
if (isConstructible) | |
generateStaticMetacall(qualifiedClassNameIdentifier); | |
// | |
// Build extra array | |
// | |
QList<QByteArray> extraList; | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
if (!isVariantType(p.type) && !metaTypes.contains(p.type) && !p.type.contains('*') && | |
!p.type.contains('<') && !p.type.contains('>')) { | |
int s = p.type.lastIndexOf("::"); | |
if (s > 0) { | |
QByteArray scope = p.type.left(s); | |
if (scope != "Qt" && scope != cdef->classname && !extraList.contains(scope)) | |
extraList += scope; | |
} | |
} | |
} | |
if (!extraList.isEmpty()) { | |
fprintf(out, "#ifdef Q_NO_DATA_RELOCATION\n"); | |
fprintf(out, "static const QMetaObjectAccessor qt_meta_extradata_%s[] = {\n ", qualifiedClassNameIdentifier.constData()); | |
for (int i = 0; i < extraList.count(); ++i) { | |
fprintf(out, " %s::getStaticMetaObject,\n", extraList.at(i).constData()); | |
} | |
fprintf(out, "#else\n"); | |
fprintf(out, "static const QMetaObject *qt_meta_extradata_%s[] = {\n ", qualifiedClassNameIdentifier.constData()); | |
for (int i = 0; i < extraList.count(); ++i) { | |
fprintf(out, " &%s::staticMetaObject,\n", extraList.at(i).constData()); | |
} | |
fprintf(out, "#endif //Q_NO_DATA_RELOCATION\n"); | |
fprintf(out, " 0\n};\n\n"); | |
} | |
if (isConstructible || !extraList.isEmpty()) { | |
fprintf(out, "static const QMetaObjectExtraData qt_meta_extradata2_%s = {\n ", | |
qualifiedClassNameIdentifier.constData()); | |
if (extraList.isEmpty()) | |
fprintf(out, "0, "); | |
else | |
fprintf(out, "qt_meta_extradata_%s, ", qualifiedClassNameIdentifier.constData()); | |
if (!isConstructible) | |
fprintf(out, "0"); | |
else | |
fprintf(out, "%s_qt_static_metacall", qualifiedClassNameIdentifier.constData()); | |
fprintf(out, " \n};\n\n"); | |
} | |
// | |
// Finally create and initialize the static meta object | |
// | |
if (isQt) | |
fprintf(out, "const QMetaObject QObject::staticQtMetaObject = {\n"); | |
else | |
fprintf(out, "const QMetaObject %s::staticMetaObject = {\n", cdef->qualified.constData()); | |
if (isQObject) | |
fprintf(out, " { 0, "); | |
else if (cdef->superclassList.size()) | |
fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); | |
else | |
fprintf(out, " { 0, "); | |
fprintf(out, "qt_meta_stringdata_%s,\n qt_meta_data_%s, ", | |
qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); | |
if (!isConstructible && extraList.isEmpty()) | |
fprintf(out, "0 }\n"); | |
else | |
fprintf(out, "&qt_meta_extradata2_%s }\n", qualifiedClassNameIdentifier.constData()); | |
fprintf(out, "};\n"); | |
if(isQt) | |
return; | |
// | |
// Generate static meta object accessor (needed for symbian, because DLLs do not support data imports. | |
// | |
fprintf(out, "\n#ifdef Q_NO_DATA_RELOCATION\n"); | |
fprintf(out, "const QMetaObject &%s::getStaticMetaObject() { return staticMetaObject; }\n", cdef->qualified.constData()); | |
fprintf(out, "#endif //Q_NO_DATA_RELOCATION\n"); | |
if (!cdef->hasQObject) | |
return; | |
fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;\n}\n", | |
cdef->qualified.constData()); | |
// | |
// Generate smart cast function | |
// | |
fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); | |
fprintf(out, " if (!_clname) return 0;\n"); | |
fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s))\n" | |
" return static_cast<void*>(const_cast< %s*>(this));\n", | |
qualifiedClassNameIdentifier.constData(), cdef->classname.constData()); | |
for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one | |
if (cdef->superclassList.at(i).second == FunctionDef::Private) | |
continue; | |
const char *cname = cdef->superclassList.at(i).first; | |
fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(const_cast< %s*>(this));\n", | |
cname, cname, cdef->classname.constData()); | |
} | |
for (int i = 0; i < cdef->interfaceList.size(); ++i) { | |
const QList<ClassDef::Interface> &iface = cdef->interfaceList.at(i); | |
for (int j = 0; j < iface.size(); ++j) { | |
fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData()); | |
for (int k = j; k >= 0; --k) | |
fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData()); | |
fprintf(out, "const_cast< %s*>(this)%s;\n", | |
cdef->classname.constData(), QByteArray(j+1, ')').constData()); | |
} | |
} | |
if (!purestSuperClass.isEmpty() && !isQObject) { | |
QByteArray superClass = purestSuperClass; | |
// workaround for VC6 | |
if (superClass.contains("::")) { | |
fprintf(out, " typedef %s QMocSuperClass;\n", superClass.constData()); | |
superClass = "QMocSuperClass"; | |
} | |
fprintf(out, " return %s::qt_metacast(_clname);\n", superClass.constData()); | |
} else { | |
fprintf(out, " return 0;\n"); | |
} | |
fprintf(out, "}\n"); | |
// | |
// Generate internal qt_metacall() function | |
// | |
generateMetacall(); | |
// | |
// Generate internal signal functions | |
// | |
for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) | |
generateSignal(&cdef->signalList[signalindex], signalindex); | |
} | |
void Generator::generateClassInfos() | |
{ | |
if (cdef->classInfoList.isEmpty()) | |
return; | |
fprintf(out, "\n // classinfo: key, value\n"); | |
for (int i = 0; i < cdef->classInfoList.size(); ++i) { | |
const ClassInfoDef &c = cdef->classInfoList.at(i); | |
fprintf(out, " %4d, %4d,\n", strreg(c.name), strreg(c.value)); | |
} | |
} | |
void Generator::generateFunctions(QList<FunctionDef>& list, const char *functype, int type) | |
{ | |
if (list.isEmpty()) | |
return; | |
fprintf(out, "\n // %ss: signature, parameters, type, tag, flags\n", functype); | |
for (int i = 0; i < list.count(); ++i) { | |
const FunctionDef &f = list.at(i); | |
QByteArray sig = f.name + '('; | |
QByteArray arguments; | |
for (int j = 0; j < f.arguments.count(); ++j) { | |
const ArgumentDef &a = f.arguments.at(j); | |
if (j) { | |
sig += ","; | |
arguments += ","; | |
} | |
sig += a.normalizedType; | |
arguments += a.name; | |
} | |
sig += ')'; | |
unsigned char flags = type; | |
if (f.access == FunctionDef::Private) | |
flags |= AccessPrivate; | |
else if (f.access == FunctionDef::Public) | |
flags |= AccessPublic; | |
else if (f.access == FunctionDef::Protected) | |
flags |= AccessProtected; | |
if (f.access == FunctionDef::Private) | |
flags |= AccessPrivate; | |
else if (f.access == FunctionDef::Public) | |
flags |= AccessPublic; | |
else if (f.access == FunctionDef::Protected) | |
flags |= AccessProtected; | |
if (f.isCompat) | |
flags |= MethodCompatibility; | |
if (f.wasCloned) | |
flags |= MethodCloned; | |
if (f.isScriptable) | |
flags |= MethodScriptable; | |
if (f.revision > 0) | |
flags |= MethodRevisioned; | |
fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", strreg(sig), | |
strreg(arguments), strreg(f.normalizedType), strreg(f.tag), flags); | |
} | |
} | |
void Generator::generateFunctionRevisions(QList<FunctionDef>& list, const char *functype) | |
{ | |
if (list.count()) | |
fprintf(out, "\n // %ss: revision\n", functype); | |
for (int i = 0; i < list.count(); ++i) { | |
const FunctionDef &f = list.at(i); | |
fprintf(out, " %4d,\n", f.revision); | |
} | |
} | |
void Generator::generateProperties() | |
{ | |
// | |
// Create meta data | |
// | |
if (cdef->propertyList.count()) | |
fprintf(out, "\n // properties: name, type, flags\n"); | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
uint flags = Invalid; | |
if (!isVariantType(p.type)) { | |
flags |= EnumOrFlag; | |
} else if (!isQRealType(p.type)) { | |
flags |= qvariant_nameToType(p.type) << 24; | |
} | |
if (!p.read.isEmpty()) | |
flags |= Readable; | |
if (!p.write.isEmpty()) { | |
flags |= Writable; | |
if (p.stdCppSet()) | |
flags |= StdCppSet; | |
} | |
if (!p.reset.isEmpty()) | |
flags |= Resettable; | |
// if (p.override) | |
// flags |= Override; | |
if (p.designable.isEmpty()) | |
flags |= ResolveDesignable; | |
else if (p.designable != "false") | |
flags |= Designable; | |
if (p.scriptable.isEmpty()) | |
flags |= ResolveScriptable; | |
else if (p.scriptable != "false") | |
flags |= Scriptable; | |
if (p.stored.isEmpty()) | |
flags |= ResolveStored; | |
else if (p.stored != "false") | |
flags |= Stored; | |
if (p.editable.isEmpty()) | |
flags |= ResolveEditable; | |
else if (p.editable != "false") | |
flags |= Editable; | |
if (p.user.isEmpty()) | |
flags |= ResolveUser; | |
else if (p.user != "false") | |
flags |= User; | |
if (p.notifyId != -1) | |
flags |= Notify; | |
if (p.revision > 0) | |
flags |= Revisioned; | |
if (p.constant) | |
flags |= Constant; | |
if (p.final) | |
flags |= Final; | |
fprintf(out, " %4d, %4d, ", | |
strreg(p.name), | |
strreg(p.type)); | |
if (!(flags >> 24) && isQRealType(p.type)) | |
fprintf(out, "(QMetaType::QReal << 24) | "); | |
fprintf(out, "0x%.8x,\n", flags); | |
} | |
if(cdef->notifyableProperties) { | |
fprintf(out, "\n // properties: notify_signal_id\n"); | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
if(p.notifyId == -1) | |
fprintf(out, " %4d,\n", | |
0); | |
else | |
fprintf(out, " %4d,\n", | |
p.notifyId); | |
} | |
} | |
if (cdef->revisionedProperties) { | |
fprintf(out, "\n // properties: revision\n"); | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
fprintf(out, " %4d,\n", p.revision); | |
} | |
} | |
} | |
void Generator::generateEnums(int index) | |
{ | |
if (cdef->enumDeclarations.isEmpty()) | |
return; | |
fprintf(out, "\n // enums: name, flags, count, data\n"); | |
index += 4 * cdef->enumList.count(); | |
int i; | |
for (i = 0; i < cdef->enumList.count(); ++i) { | |
const EnumDef &e = cdef->enumList.at(i); | |
fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n", | |
strreg(e.name), | |
cdef->enumDeclarations.value(e.name) ? 1 : 0, | |
e.values.count(), | |
index); | |
index += e.values.count() * 2; | |
} | |
fprintf(out, "\n // enum data: key, value\n"); | |
for (i = 0; i < cdef->enumList.count(); ++i) { | |
const EnumDef &e = cdef->enumList.at(i); | |
for (int j = 0; j < e.values.count(); ++j) { | |
const QByteArray &val = e.values.at(j); | |
fprintf(out, " %4d, uint(%s::%s),\n", | |
strreg(val), | |
cdef->qualified.constData(), | |
val.constData()); | |
} | |
} | |
} | |
void Generator::generateMetacall() | |
{ | |
bool isQObject = (cdef->classname == "QObject"); | |
fprintf(out, "\nint %s::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n", | |
cdef->qualified.constData()); | |
if (!purestSuperClass.isEmpty() && !isQObject) { | |
QByteArray superClass = purestSuperClass; | |
// workaround for VC6 | |
if (superClass.contains("::")) { | |
fprintf(out, " typedef %s QMocSuperClass;\n", superClass.constData()); | |
superClass = "QMocSuperClass"; | |
} | |
fprintf(out, " _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData()); | |
} | |
fprintf(out, " if (_id < 0)\n return _id;\n"); | |
fprintf(out, " "); | |
bool needElse = false; | |
QList<FunctionDef> methodList; | |
methodList += cdef->signalList; | |
methodList += cdef->slotList; | |
methodList += cdef->methodList; | |
if (methodList.size()) { | |
needElse = true; | |
fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n "); | |
fprintf(out, "switch (_id) {\n"); | |
for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { | |
const FunctionDef &f = methodList.at(methodindex); | |
fprintf(out, " case %d: ", methodindex); | |
if (f.normalizedType.size()) | |
fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); | |
if (f.inPrivateClass.size()) | |
fprintf(out, "%s->", f.inPrivateClass.constData()); | |
fprintf(out, "%s(", f.name.constData()); | |
int offset = 1; | |
for (int j = 0; j < f.arguments.count(); ++j) { | |
const ArgumentDef &a = f.arguments.at(j); | |
if (j) | |
fprintf(out, ","); | |
fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); | |
} | |
fprintf(out, ");"); | |
if (f.normalizedType.size()) | |
fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", | |
noRef(f.normalizedType).constData()); | |
fprintf(out, " break;\n"); | |
} | |
fprintf(out, " default: ;\n"); | |
fprintf(out, " }\n"); | |
} | |
if (methodList.size()) | |
fprintf(out, " _id -= %d;\n }", methodList.size()); | |
if (cdef->propertyList.size()) { | |
bool needGet = false; | |
bool needTempVarForGet = false; | |
bool needSet = false; | |
bool needReset = false; | |
bool needDesignable = false; | |
bool needScriptable = false; | |
bool needStored = false; | |
bool needEditable = false; | |
bool needUser = false; | |
for (int i = 0; i < cdef->propertyList.size(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
needGet |= !p.read.isEmpty(); | |
if (!p.read.isEmpty()) | |
needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec | |
&& p.gspec != PropertyDef::ReferenceSpec); | |
needSet |= !p.write.isEmpty(); | |
needReset |= !p.reset.isEmpty(); | |
needDesignable |= p.designable.endsWith(')'); | |
needScriptable |= p.scriptable.endsWith(')'); | |
needStored |= p.stored.endsWith(')'); | |
needEditable |= p.editable.endsWith(')'); | |
needUser |= p.user.endsWith(')'); | |
} | |
fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); | |
if (needElse) | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); | |
if (needGet) { | |
if (needTempVarForGet) | |
fprintf(out, " void *_v = _a[0];\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (p.read.isEmpty()) | |
continue; | |
QByteArray prefix; | |
if (p.inPrivateClass.size()) { | |
prefix = p.inPrivateClass; | |
prefix.append("->"); | |
} | |
if (p.gspec == PropertyDef::PointerSpec) | |
fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n", | |
propindex, prefix.constData(), p.read.constData()); | |
else if (p.gspec == PropertyDef::ReferenceSpec) | |
fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(&%s%s())); break;\n", | |
propindex, prefix.constData(), p.read.constData()); | |
else if (cdef->enumDeclarations.value(p.type, false)) | |
fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n", | |
propindex, prefix.constData(), p.read.constData()); | |
else | |
fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", | |
propindex, p.type.constData(), prefix.constData(), p.read.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n"); | |
if (needSet) { | |
fprintf(out, " void *_v = _a[0];\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (p.write.isEmpty()) | |
continue; | |
QByteArray prefix; | |
if (p.inPrivateClass.size()) { | |
prefix = p.inPrivateClass; | |
prefix.append("->"); | |
} | |
if (cdef->enumDeclarations.value(p.type, false)) { | |
fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n", | |
propindex, prefix.constData(), p.write.constData()); | |
} else { | |
fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", | |
propindex, prefix.constData(), p.write.constData(), p.type.constData()); | |
} | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n"); | |
if (needReset) { | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.reset.endsWith(')')) | |
continue; | |
QByteArray prefix; | |
if (p.inPrivateClass.size()) { | |
prefix = p.inPrivateClass; | |
prefix.append("->"); | |
} | |
fprintf(out, " case %d: %s%s; break;\n", | |
propindex, prefix.constData(), p.reset.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::QueryPropertyDesignable) {\n"); | |
if (needDesignable) { | |
fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.designable.endsWith(')')) | |
continue; | |
fprintf(out, " case %d: *_b = %s; break;\n", | |
propindex, p.designable.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::QueryPropertyScriptable) {\n"); | |
if (needScriptable) { | |
fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.scriptable.endsWith(')')) | |
continue; | |
fprintf(out, " case %d: *_b = %s; break;\n", | |
propindex, p.scriptable.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::QueryPropertyStored) {\n"); | |
if (needStored) { | |
fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.stored.endsWith(')')) | |
continue; | |
fprintf(out, " case %d: *_b = %s; break;\n", | |
propindex, p.stored.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::QueryPropertyEditable) {\n"); | |
if (needEditable) { | |
fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.editable.endsWith(')')) | |
continue; | |
fprintf(out, " case %d: *_b = %s; break;\n", | |
propindex, p.editable.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, " else "); | |
fprintf(out, "if (_c == QMetaObject::QueryPropertyUser) {\n"); | |
if (needUser) { | |
fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { | |
const PropertyDef &p = cdef->propertyList.at(propindex); | |
if (!p.user.endsWith(')')) | |
continue; | |
fprintf(out, " case %d: *_b = %s; break;\n", | |
propindex, p.user.constData()); | |
} | |
fprintf(out, " }\n"); | |
} | |
fprintf(out, | |
" _id -= %d;\n" | |
" }", cdef->propertyList.count()); | |
fprintf(out, "\n#endif // QT_NO_PROPERTIES"); | |
} | |
if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size()) | |
fprintf(out, "\n "); | |
fprintf(out,"return _id;\n}\n"); | |
} | |
void Generator::generateStaticMetacall(const QByteArray &prefix) | |
{ | |
bool isQObject = (cdef->classname == "QObject"); | |
fprintf(out, "static int %s_qt_static_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n", | |
prefix.constData()); | |
fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n"); | |
fprintf(out, " switch (_id) {\n"); | |
for (int ctorindex = 0; ctorindex < cdef->constructorList.count(); ++ctorindex) { | |
fprintf(out, " case %d: { %s *_r = new %s(", ctorindex, | |
cdef->qualified.constData(), cdef->qualified.constData()); | |
const FunctionDef &f = cdef->constructorList.at(ctorindex); | |
int offset = 1; | |
for (int j = 0; j < f.arguments.count(); ++j) { | |
const ArgumentDef &a = f.arguments.at(j); | |
if (j) | |
fprintf(out, ","); | |
fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++); | |
} | |
fprintf(out, ");\n"); | |
fprintf(out, " if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;\n"); | |
} | |
fprintf(out, " }\n"); | |
fprintf(out, " _id -= %d;\n", cdef->constructorList.count()); | |
fprintf(out, " return _id;\n"); | |
fprintf(out, " }\n"); | |
if (!isQObject) | |
fprintf(out, " _id = %s::staticMetaObject.superClass()->static_metacall(_c, _id, _a);\n", cdef->qualified.constData()); | |
fprintf(out, " if (_id < 0)\n return _id;\n"); | |
fprintf(out, " return _id;\n"); | |
fprintf(out, "}\n\n"); | |
} | |
void Generator::generateSignal(FunctionDef *def,int index) | |
{ | |
if (def->wasCloned || def->isAbstract) | |
return; | |
fprintf(out, "\n// SIGNAL %d\n%s %s::%s(", | |
index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData()); | |
QByteArray thisPtr = "this"; | |
const char *constQualifier = ""; | |
if (def->isConst) { | |
thisPtr = "const_cast< "; | |
thisPtr += cdef->qualified; | |
thisPtr += " *>(this)"; | |
constQualifier = "const"; | |
} | |
if (def->arguments.isEmpty() && def->normalizedType.isEmpty()) { | |
fprintf(out, ")%s\n{\n" | |
" QMetaObject::activate(%s, &staticMetaObject, %d, 0);\n" | |
"}\n", constQualifier, thisPtr.constData(), index); | |
return; | |
} | |
int offset = 1; | |
for (int j = 0; j < def->arguments.count(); ++j) { | |
const ArgumentDef &a = def->arguments.at(j); | |
if (j) | |
fprintf(out, ", "); | |
fprintf(out, "%s _t%d%s", a.type.name.constData(), offset++, a.rightType.constData()); | |
} | |
fprintf(out, ")%s\n{\n", constQualifier); | |
if (def->type.name.size() && def->normalizedType.size()) | |
fprintf(out, " %s _t0;\n", noRef(def->normalizedType).constData()); | |
fprintf(out, " void *_a[] = { "); | |
if (def->normalizedType.isEmpty()) { | |
fprintf(out, "0"); | |
} else { | |
if (def->returnTypeIsVolatile) | |
fprintf(out, "const_cast<void*>(reinterpret_cast<const volatile void*>(&_t0))"); | |
else | |
fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(&_t0))"); | |
} | |
int i; | |
for (i = 1; i < offset; ++i) | |
if (def->arguments.at(i - 1).type.isVolatile) | |
fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(&_t%d))", i); | |
else | |
fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i); | |
fprintf(out, " };\n"); | |
fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); | |
if (def->normalizedType.size()) | |
fprintf(out, " return _t0;\n"); | |
fprintf(out, "}\n"); | |
} | |
// | |
// Functions used when generating QMetaObject directly | |
// | |
// Much of this code is copied from the corresponding | |
// C++ code-generating functions; we can change the | |
// two generators so that more of the code is shared. | |
// The key difference from the C++ code generator is | |
// that instead of calling fprintf(), we append bytes | |
// to a buffer. | |
// | |
QMetaObject *Generator::generateMetaObject(bool ignoreProperties) | |
{ | |
// | |
// build the data array | |
// | |
// filter out undeclared enumerators and sets | |
{ | |
QList<EnumDef> enumList; | |
for (int i = 0; i < cdef->enumList.count(); ++i) { | |
EnumDef def = cdef->enumList.at(i); | |
if (cdef->enumDeclarations.contains(def.name)) { | |
enumList += def; | |
} | |
QByteArray alias = cdef->flagAliases.value(def.name); | |
if (cdef->enumDeclarations.contains(alias)) { | |
def.name = alias; | |
enumList += def; | |
} | |
} | |
cdef->enumList = enumList; | |
} | |
int index = 10; | |
meta_data | |
<< 1 // revision | |
<< strreg(cdef->qualified) // classname | |
<< cdef->classInfoList.count() << (cdef->classInfoList.count() ? index : 0) // classinfo | |
; | |
index += cdef->classInfoList.count() * 2; | |
int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count(); | |
meta_data << methodCount << (methodCount ? index : 0); // methods | |
index += methodCount * 5; | |
if (!ignoreProperties) { | |
meta_data << cdef->propertyList.count() << (cdef->propertyList.count() ? index : 0); // properties | |
index += cdef->propertyList.count() * 3; | |
} else { | |
meta_data << 0 << 0; // properties | |
} | |
meta_data << cdef->enumList.count() << (cdef->enumList.count() ? index : 0); // enums/sets | |
// | |
// Build classinfo array | |
// | |
_generateClassInfos(); | |
// | |
// Build signals array first, otherwise the signal indices would be wrong | |
// | |
_generateFunctions(cdef->signalList, MethodSignal); | |
// | |
// Build slots array | |
// | |
_generateFunctions(cdef->slotList, MethodSlot); | |
// | |
// Build method array | |
// | |
_generateFunctions(cdef->methodList, MethodMethod); | |
// | |
// Build property array | |
// | |
if (!ignoreProperties) | |
_generateProperties(); | |
// | |
// Build enums array | |
// | |
_generateEnums(index); | |
// | |
// Terminate data array | |
// | |
meta_data << 0; | |
// | |
// Build stringdata array | |
// | |
QVector<char> string_data; | |
for (int i = 0; i < strings.size(); ++i) { | |
const char *s = strings.at(i).constData(); | |
char c; | |
do { | |
c = *(s++); | |
string_data << c; | |
} while (c != '\0'); | |
} | |
// | |
// Finally create and initialize the static meta object | |
// | |
const int meta_object_offset = 0; | |
const int meta_object_size = sizeof(QMetaObject); | |
const int meta_data_offset = meta_object_offset + meta_object_size; | |
const int meta_data_size = meta_data.count() * sizeof(uint); | |
const int string_data_offset = meta_data_offset + meta_data_size; | |
const int string_data_size = string_data.count(); | |
const int total_size = string_data_offset + string_data_size; | |
char *blob = new char[total_size]; | |
char *string_data_output = blob + string_data_offset; | |
const char *string_data_src = string_data.constData(); | |
for (int i = 0; i < string_data.count(); ++i) | |
string_data_output[i] = string_data_src[i]; | |
uint *meta_data_output = reinterpret_cast<uint *>(blob + meta_data_offset); | |
const uint *meta_data_src = meta_data.constData(); | |
for (int i = 0; i < meta_data.count(); ++i) | |
meta_data_output[i] = meta_data_src[i]; | |
QMetaObject *meta_object = new (blob + meta_object_offset)QMetaObject; | |
meta_object->d.superdata = 0; | |
meta_object->d.stringdata = string_data_output; | |
meta_object->d.data = meta_data_output; | |
meta_object->d.extradata = 0; | |
return meta_object; | |
} | |
void Generator::_generateClassInfos() | |
{ | |
for (int i = 0; i < cdef->classInfoList.size(); ++i) { | |
const ClassInfoDef &c = cdef->classInfoList.at(i); | |
meta_data << strreg(c.name) << strreg(c.value); | |
} | |
} | |
void Generator::_generateFunctions(QList<FunctionDef> &list, int type) | |
{ | |
for (int i = 0; i < list.count(); ++i) { | |
const FunctionDef &f = list.at(i); | |
QByteArray sig = f.name + '('; | |
QByteArray arguments; | |
for (int j = 0; j < f.arguments.count(); ++j) { | |
const ArgumentDef &a = f.arguments.at(j); | |
if (j) { | |
sig += ','; | |
arguments += ','; | |
} | |
sig += a.normalizedType; | |
arguments += a.name; | |
} | |
sig += ')'; | |
char flags = type; | |
if (f.access == FunctionDef::Private) | |
flags |= AccessPrivate; | |
else if (f.access == FunctionDef::Public) | |
flags |= AccessPublic; | |
else if (f.access == FunctionDef::Protected) | |
flags |= AccessProtected; | |
if (f.access == FunctionDef::Private) | |
flags |= AccessPrivate; | |
else if (f.access == FunctionDef::Public) | |
flags |= AccessPublic; | |
else if (f.access == FunctionDef::Protected) | |
flags |= AccessProtected; | |
if (f.isCompat) | |
flags |= MethodCompatibility; | |
if (f.wasCloned) | |
flags |= MethodCloned; | |
if (f.isScriptable) | |
flags |= MethodScriptable; | |
meta_data << strreg(sig) | |
<< strreg(arguments) | |
<< strreg(f.normalizedType) | |
<< strreg(f.tag) | |
<< flags; | |
} | |
} | |
void Generator::_generateEnums(int index) | |
{ | |
index += 4 * cdef->enumList.count(); | |
int i; | |
for (i = 0; i < cdef->enumList.count(); ++i) { | |
const EnumDef &e = cdef->enumList.at(i); | |
meta_data << strreg(e.name) << (cdef->enumDeclarations.value(e.name) ? 1 : 0) | |
<< e.values.count() << index; | |
index += e.values.count() * 2; | |
} | |
for (i = 0; i < cdef->enumList.count(); ++i) { | |
const EnumDef &e = cdef->enumList.at(i); | |
for (int j = 0; j < e.values.count(); ++j) { | |
const QByteArray &val = e.values.at(j); | |
meta_data << strreg(val) << 0; // we don't know the value itself | |
} | |
} | |
} | |
void Generator::_generateProperties() | |
{ | |
// | |
// specify get function, for compatibiliy we accept functions | |
// returning pointers, or const char * for QByteArray. | |
// | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
PropertyDef &p = cdef->propertyList[i]; | |
if (p.read.isEmpty()) | |
continue; | |
for (int j = 0; j < cdef->publicList.count(); ++j) { | |
const FunctionDef &f = cdef->publicList.at(j); | |
if (f.name != p.read) | |
continue; | |
if (!f.isConst) // get functions must be const | |
continue; | |
if (f.arguments.size()) // and must not take any arguments | |
continue; | |
PropertyDef::Specification spec = PropertyDef::ValueSpec; | |
QByteArray tmp = f.normalizedType; | |
if (p.type == "QByteArray" && tmp == "const char *") | |
tmp = "QByteArray"; | |
if (tmp.left(6) == "const ") | |
tmp = tmp.mid(6); | |
if (p.type != tmp && tmp.endsWith('*')) { | |
tmp.chop(1); | |
spec = PropertyDef::PointerSpec; | |
} else if (f.type.name.endsWith('&')) { // raw type, not normalized type | |
spec = PropertyDef::ReferenceSpec; | |
} | |
if (p.type != tmp) | |
continue; | |
p.gspec = spec; | |
break; | |
} | |
} | |
// | |
// Create meta data | |
// | |
for (int i = 0; i < cdef->propertyList.count(); ++i) { | |
const PropertyDef &p = cdef->propertyList.at(i); | |
uint flags = Invalid; | |
if (!isVariantType(p.type)) { | |
flags |= EnumOrFlag; | |
} else { | |
flags |= qvariant_nameToType(p.type) << 24; | |
} | |
if (!p.read.isEmpty()) | |
flags |= Readable; | |
if (!p.write.isEmpty()) { | |
flags |= Writable; | |
if (p.stdCppSet()) | |
flags |= StdCppSet; | |
} | |
if (!p.reset.isEmpty()) | |
flags |= Resettable; | |
// if (p.override) | |
// flags |= Override; | |
if (p.designable.isEmpty()) | |
flags |= ResolveDesignable; | |
else if (p.designable != "false") | |
flags |= Designable; | |
if (p.scriptable.isEmpty()) | |
flags |= ResolveScriptable; | |
else if (p.scriptable != "false") | |
flags |= Scriptable; | |
if (p.stored.isEmpty()) | |
flags |= ResolveStored; | |
else if (p.stored != "false") | |
flags |= Stored; | |
if (p.editable.isEmpty()) | |
flags |= ResolveEditable; | |
else if (p.editable != "false") | |
flags |= Editable; | |
if (p.user.isEmpty()) | |
flags |= ResolveUser; | |
else if (p.user != "false") | |
flags |= User; | |
meta_data << strreg(p.name) << strreg(p.type) << flags; | |
} | |
} | |
QT_END_NAMESPACE |