From d9fb4e37573e55d85b1cfbb24c94d48d93350a54 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 29 Jan 2016 12:02:04 +0100 Subject: Change staticMetaObject generation. The full meta-object generation is replaced with the moc generator. This gives a fully functional meta-object without any overhead during object instantiation. Change-Id: Ibf62a6f1bfc0873cd00dd2e4bbad38c8d165005a Reviewed-by: Nikolai Kosjar --- tools/qscxmlc/generator.cpp | 1616 ++++++++++++++++++++++++++++++++++++++ tools/qscxmlc/generator.h | 78 ++ tools/qscxmlc/moc.h | 201 +++++ tools/qscxmlc/outputrevision.h | 35 + tools/qscxmlc/qscxmlc.pro | 4 +- tools/qscxmlc/scxmlcppdumper.cpp | 272 ++++++- tools/qscxmlc/utils.h | 121 +++ 7 files changed, 2284 insertions(+), 43 deletions(-) create mode 100644 tools/qscxmlc/generator.cpp create mode 100644 tools/qscxmlc/generator.h create mode 100644 tools/qscxmlc/moc.h create mode 100644 tools/qscxmlc/outputrevision.h create mode 100644 tools/qscxmlc/utils.h (limited to 'tools') diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp new file mode 100644 index 0000000..88688f9 --- /dev/null +++ b/tools/qscxmlc/generator.cpp @@ -0,0 +1,1616 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generator.h" +#include "outputrevision.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include + +#include //for the flags. + +QT_BEGIN_NAMESPACE + +void fprintf(QTextStream &out, const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + const int bufSize = 4096; + char buf[bufSize]; + vsnprintf(buf, bufSize, fmt, argp); + out << buf; +} + +void fputc(char c, QTextStream &out) +{ + out << c; +} + +void fputs(const char *s, QTextStream &out) +{ + out << s; +} + +uint nameToBuiltinType(const QByteArray &name) +{ + if (name.isEmpty()) + return 0; + + uint tp = QMetaType::type(name.constData()); + return tp < uint(QMetaType::User) ? tp : uint(QMetaType::UnknownType); +} + +/* + Returns \c true if the type is a built-in type. +*/ +bool isBuiltinType(const QByteArray &type) + { + int id = QMetaType::type(type.constData()); + if (id == QMetaType::UnknownType) + return false; + return (id < QMetaType::User); +} + +static const char *metaTypeEnumValueString(int type) + { +#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType) \ + case QMetaType::MetaTypeName: return #MetaTypeName; + + switch (type) { +QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) + } +#undef RETURN_METATYPENAME_STRING + return 0; + } + +Generator::Generator(ClassDef *classDef, const QList &metaTypes, const QHash &knownQObjectClasses, const QHash &knownGadgets, QTextStream &outfile) + : out(outfile), cdef(classDef), metaTypes(metaTypes), knownQObjectClasses(knownQObjectClasses) + , knownGadgets(knownGadgets) +{ + 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; +} + +void Generator::strreg(const QByteArray &s) +{ + if (!strings.contains(s)) + strings.append(s); +} + +int Generator::stridx(const QByteArray &s) +{ + int i = strings.indexOf(s); + Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); + return i; +} + +// Returns the sum of all parameters (including return type) for the given +// \a list of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList &list) +{ + int sum = 0; + for (int i = 0; i < list.count(); ++i) + sum += list.at(i).arguments.count() + 1; // +1 for return type + return sum; +} + +bool Generator::registerableMetaType(const QByteArray &propertyType) +{ + if (metaTypes.contains(propertyType)) + return true; + + if (propertyType.endsWith('*')) { + QByteArray objectPointerType = propertyType; + // The objects container stores class names, such as 'QState', 'QLabel' etc, + // not 'QState*', 'QLabel*'. The propertyType does contain the '*', so we need + // to chop it to find the class type in the known QObjects list. + objectPointerType.chop(1); + if (knownQObjectClasses.contains(objectPointerType)) + return true; + } + + static const QVector smartPointers = QVector() +#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER + QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) +#undef STREAM_SMART_POINTER + ; + + foreach (const QByteArray &smartPointer, smartPointers) + if (propertyType.startsWith(smartPointer + "<") && !propertyType.endsWith("&")) + return knownQObjectClasses.contains(propertyType.mid(smartPointer.size() + 1, propertyType.size() - smartPointer.size() - 1 - 1)); + + static const QVector oneArgTemplates = QVector() +#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME + QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) +#undef STREAM_1ARG_TEMPLATE + ; + foreach (const QByteArray &oneArgTemplateType, oneArgTemplates) + if (propertyType.startsWith(oneArgTemplateType + "<") && propertyType.endsWith(">")) { + const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1 + // The closing '>' + - 1 + // templates inside templates have an extra whitespace char to strip. + - (propertyType.at(propertyType.size() - 2) == ' ' ? 1 : 0 ); + const QByteArray templateArg = propertyType.mid(oneArgTemplateType.size() + 1, argumentSize); + return isBuiltinType(templateArg) || registerableMetaType(templateArg); + } + return false; +} + +/* returns \c true if name and qualifiedName refers to the same name. + * If qualified name is "A::B::C", it returns \c true for "C", "B::C" or "A::B::C" */ +static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArray &name) +{ + if (qualifiedName == name) + return true; + int index = qualifiedName.indexOf("::"); + if (index == -1) + return false; + return qualifiedNameEquals(qualifiedName.mid(index+2), name); +} + +void Generator::generateCode() +{ + bool isQt = (cdef->classname == "Qt"); + bool isQObject = (cdef->classname == "QObject"); + bool isConstructible = !cdef->constructorList.isEmpty(); + + // filter out undeclared enumerators and sets + { + QList 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; + } + +// +// Register all strings used in data section +// + strreg(cdef->qualified); + registerClassInfoStrings(); + registerFunctionStrings(cdef->signalList); + registerFunctionStrings(cdef->slotList); + registerFunctionStrings(cdef->methodList); + registerFunctionStrings(cdef->constructorList); + registerPropertyStrings(); + registerEnumStrings(); + + QByteArray qualifiedClassNameIdentifier = cdef->qualified; + qualifiedClassNameIdentifier.replace(':', '_'); + +// +// Build stringdata struct +// + const int constCharArraySizeLimit = 65535; + fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData()); + fprintf(out, " QByteArrayData data[%d];\n", strings.size()); + { + int stringDataLength = 0; + int stringDataCounter = 0; + for (int i = 0; i < strings.size(); ++i) { + int thisLength = strings.at(i).length() + 1; + stringDataLength += thisLength; + if (stringDataLength / constCharArraySizeLimit) { + // save previous stringdata and start computing the next one. + fprintf(out, " char stringdata%d[%d];\n", stringDataCounter++, stringDataLength - thisLength); + stringDataLength = thisLength; + } + } + fprintf(out, " char stringdata%d[%d];\n", stringDataCounter, stringDataLength); + + } + fprintf(out, "};\n"); + + // Macro that expands into a QByteArrayData. The offset member is + // calculated from 1) the offset of the actual characters in the + // stringdata.stringdata member, and 2) the stringdata.data index of the + // QByteArrayData being defined. This calculation relies on the + // QByteArrayData::data() implementation returning simply "this + offset". + fprintf(out, "#define QT_MOC_LITERAL(idx, ofs, len) \\\n" + " Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \\\n" + " qptrdiff(offsetof(qt_meta_stringdata_%s_t, stringdata0) + ofs \\\n" + " - idx * sizeof(QByteArrayData)) \\\n" + " )\n", + qualifiedClassNameIdentifier.constData()); + + fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n", + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + fprintf(out, " {\n"); + { + int idx = 0; + for (int i = 0; i < strings.size(); ++i) { + const QByteArray &str = strings.at(i); + fprintf(out, "QT_MOC_LITERAL(%d, %d, %d)", i, idx, str.length()); + if (i != strings.size() - 1) + fputc(',', out); + const QByteArray comment = str.length() > 32 ? str.left(29) + "..." : str; + fprintf(out, " // \"%s\"\n", comment.constData()); + idx += str.length() + 1; + for (int j = 0; j < str.length(); ++j) { + if (str.at(j) == '\\') { + int cnt = lengthOfEscapeSequence(str, j) - 1; + idx -= cnt; + j += cnt; + } + } + } + fprintf(out, "\n },\n"); + } + +// +// Build stringdata array +// + fprintf(out, " \""); + int col = 0; + int len = 0; + int stringDataLength = 0; + for (int i = 0; i < strings.size(); ++i) { + QByteArray s = strings.at(i); + len = s.length(); + stringDataLength += len + 1; + if (stringDataLength >= constCharArraySizeLimit) { + fprintf(out, "\",\n \""); + stringDataLength = len + 1; + col = 0; + } else if (i) + fputs("\\0", out); // add \0 at the end of each string + + 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); + } + fprintf(out, "%.*s", spanLen, s.constData() + idx); + idx += spanLen; + col += spanLen; + } + col += len + 2; + } + +// Terminate stringdata struct + fprintf(out, "\"\n};\n"); + fprintf(out, "#undef QT_MOC_LITERAL\n\n"); + +// +// build the data array +// + + int index = MetaObjectPrivateFieldCount; + fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); + fprintf(out, "\n // content:\n"); + fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision)); + fprintf(out, " %4d, // classname\n", stridx(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; + int paramsIndex = index; + int totalParameterCount = aggregateParameterCount(cdef->signalList) + + aggregateParameterCount(cdef->slotList) + + aggregateParameterCount(cdef->methodList) + + aggregateParameterCount(cdef->constructorList); + index += totalParameterCount * 2 // types and parameter names + - methodCount // return "parameters" don't have names + - cdef->constructorList.count(); // "this" parameters don't have names + + 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 (int 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); + + int flags = 0; + if (cdef->hasQGadget) { + // Ideally, all the classes could have that flag. But this broke classes generated + // by qdbusxml2cpp which generate code that require that we call qt_metacall for properties + flags |= PropertyAccessInStaticMetaCall; + } + fprintf(out, " %4d, // flags\n", flags); + 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, paramsIndex); + +// +// Build slots array +// + generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex); + +// +// Build method array +// + generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex); + +// +// Build method version arrays +// + if (cdef->revisionedMethods) { + generateFunctionRevisions(cdef->signalList, "signal"); + generateFunctionRevisions(cdef->slotList, "slot"); + generateFunctionRevisions(cdef->methodList, "method"); + } + +// +// Build method parameters array +// + generateFunctionParameters(cdef->signalList, "signal"); + generateFunctionParameters(cdef->slotList, "slot"); + generateFunctionParameters(cdef->methodList, "method"); + if (isConstructible) + generateFunctionParameters(cdef->constructorList, "constructor"); + +// +// Build property array +// + generateProperties(); + +// +// Build enums array +// + generateEnums(enumsIndex); + +// +// Build constructors array +// + if (isConstructible) + generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex); + +// +// Terminate data array +// + fprintf(out, "\n 0 // eod\n};\n\n"); + +// +// Generate internal qt_static_metacall() function +// + const bool hasStaticMetaCall = !isQt && + (cdef->hasQObject || !cdef->methodList.isEmpty() + || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty()); + if (hasStaticMetaCall) + generateStaticMetacall(); + +// +// Build extra array +// + QList extraList; + QHash knownExtraMetaObject = knownGadgets; + knownExtraMetaObject.unite(knownQObjectClasses); + + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + if (isBuiltinType(p.type)) + continue; + + if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>')) + continue; + + int s = p.type.lastIndexOf("::"); + if (s <= 0) + continue; + + QByteArray unqualifiedScope = p.type.left(s); + + // The scope may be a namespace for example, so it's only safe to include scopes that are known QObjects (QTBUG-2151) + QHash::ConstIterator scopeIt; + + QByteArray thisScope = cdef->qualified; + do { + int s = thisScope.lastIndexOf("::"); + thisScope = thisScope.left(s); + QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope; + scopeIt = knownExtraMetaObject.constFind(currentScope); + } while (!thisScope.isEmpty() && scopeIt == knownExtraMetaObject.constEnd()); + + if (scopeIt == knownExtraMetaObject.constEnd()) + continue; + + const QByteArray &scope = *scopeIt; + + if (scope == "Qt") + continue; + if (qualifiedNameEquals(cdef->qualified, scope)) + continue; + + if (!extraList.contains(scope)) + extraList += scope; + } + + // QTBUG-20639 - Accept non-local enums for QML signal/slot parameters. + // Look for any scoped enum declarations, and add those to the list + // of extra/related metaobjects for this object. + QList enumKeys = cdef->enumDeclarations.keys(); + for (int i = 0; i < enumKeys.count(); ++i) { + const QByteArray &enumKey = enumKeys[i]; + int s = enumKey.lastIndexOf("::"); + if (s > 0) { + QByteArray scope = enumKey.left(s); + if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope)) + extraList += scope; + } + } + + if (!extraList.isEmpty()) { + fprintf(out, "static const QMetaObject * const 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, " Q_NULLPTR\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, " { Q_NULLPTR, "); + else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass))) + fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); + else + fprintf(out, " { Q_NULLPTR, "); + fprintf(out, "qt_meta_stringdata_%s.data,\n" + " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), + qualifiedClassNameIdentifier.constData()); + if (hasStaticMetaCall) + fprintf(out, " qt_static_metacall, "); + else + fprintf(out, " Q_NULLPTR, "); + + if (extraList.isEmpty()) + fprintf(out, "Q_NULLPTR, "); + else + fprintf(out, "qt_meta_extradata_%s, ", qualifiedClassNameIdentifier.constData()); + fprintf(out, "Q_NULLPTR}\n};\n\n"); + + if(isQt) + return; + + if (!cdef->hasQObject) + return; + + fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &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 Q_NULLPTR;\n"); + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n" + " return static_cast(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.constData(); + 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 &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; + fprintf(out, " return %s::qt_metacast(_clname);\n", superClass.constData()); + } else { + fprintf(out, " return Q_NULLPTR;\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); + +// +// Generate plugin meta data +// +// generatePluginMetaData(); +} + + +void Generator::registerClassInfoStrings() +{ + for (int i = 0; i < cdef->classInfoList.size(); ++i) { + const ClassInfoDef &c = cdef->classInfoList.at(i); + strreg(c.name); + strreg(c.value); + } +} + +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", stridx(c.name), stridx(c.value)); + } +} + +void Generator::registerFunctionStrings(const QList& list) +{ + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + + strreg(f.name); + if (!isBuiltinType(f.normalizedType)) + strreg(f.normalizedType); + strreg(f.tag); + + int argsCount = f.arguments.count(); + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (!isBuiltinType(a.normalizedType)) + strreg(a.normalizedType); + strreg(a.name); + } + } +} + +void Generator::generateFunctions(const QList& list, const char *functype, int type, int ¶msIndex) +{ + if (list.isEmpty()) + return; + fprintf(out, "\n // %ss: name, argc, parameters, tag, flags\n", functype); + + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + + QByteArray comment; + unsigned char flags = type; + if (f.access == FunctionDef::Private) { + flags |= AccessPrivate; + comment.append("Private"); + } else if (f.access == FunctionDef::Public) { + flags |= AccessPublic; + comment.append("Public"); + } else if (f.access == FunctionDef::Protected) { + flags |= AccessProtected; + comment.append("Protected"); + } + if (f.isCompat) { + flags |= MethodCompatibility; + comment.append(" | MethodCompatibility"); + } + if (f.wasCloned) { + flags |= MethodCloned; + comment.append(" | MethodCloned"); + } + if (f.isScriptable) { + flags |= MethodScriptable; + comment.append(" | isScriptable"); + } + if (f.revision > 0) { + flags |= MethodRevisioned; + comment.append(" | MethodRevisioned"); + } + + int argc = f.arguments.count(); + fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x /* %s */,\n", + stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, comment.constData()); + + paramsIndex += 1 + argc * 2; + } +} + +void Generator::generateFunctionRevisions(const QList& 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::generateFunctionParameters(const QList& list, const char *functype) +{ + if (list.isEmpty()) + return; + fprintf(out, "\n // %ss: parameters\n", functype); + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + fprintf(out, " "); + + // Types + int argsCount = f.arguments.count(); + for (int j = -1; j < argsCount; ++j) { + if (j > -1) + fputc(' ', out); + const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType; + generateTypeInfo(typeName, /*allowEmptyName=*/f.isConstructor); + fputc(',', out); + } + + // Parameter names + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &arg = f.arguments.at(j); + fprintf(out, " %4d,", stridx(arg.name)); + } + + fprintf(out, "\n"); + } +} + +void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName) +{ + Q_UNUSED(allowEmptyName); + if (isBuiltinType(typeName)) { + int type; + const char *valueString; + if (typeName == "qreal") { + type = QMetaType::UnknownType; + valueString = "QReal"; + } else { + type = nameToBuiltinType(typeName); + valueString = metaTypeEnumValueString(type); + } + if (valueString) { + fprintf(out, "QMetaType::%s", valueString); + } else { + Q_ASSERT(type != QMetaType::UnknownType); + fprintf(out, "%4d", type); + } + } else { + Q_ASSERT(!typeName.isEmpty() || allowEmptyName); + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName)); + } +} + +void Generator::registerPropertyStrings() +{ + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + strreg(p.name); + if (!isBuiltinType(p.type)) + strreg(p.type); + } +} + +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 (!isBuiltinType(p.type)) + flags |= EnumOrFlag; + if (!p.member.isEmpty() && !p.constant) + flags |= Writable; + if (!p.read.isEmpty() || !p.member.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, ", stridx(p.name)); + generateTypeInfo(p.type); + 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::registerEnumStrings() +{ + for (int i = 0; i < cdef->enumList.count(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + strreg(e.name); + for (int j = 0; j < e.values.count(); ++j) + strreg(e.values.at(j)); + } +} + +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", + stridx(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); + QByteArray code = cdef->qualified.constData(); + if (e.isEnumClass) + code += "::" + e.name; + code += "::" + val; + fprintf(out, " %4d, uint(%s),\n", + stridx(val), code.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; + 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 methodList; + methodList += cdef->signalList; + methodList += cdef->slotList; + methodList += cdef->methodList; + + if (methodList.size()) { + needElse = true; + fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); + fprintf(out, " if (_id < %d)\n", methodList.size()); + fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); + fprintf(out, " _id -= %d;\n }", methodList.size()); + + fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); + fprintf(out, " if (_id < %d)\n", methodList.size()); + + if (methodsWithAutomaticTypesHelper(methodList).isEmpty()) + fprintf(out, " *reinterpret_cast(_a[0]) = -1;\n"); + else + fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); + fprintf(out, " _id -= %d;\n }", methodList.size()); + + } + + if (cdef->propertyList.size()) { + 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); + 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 || _c == QMetaObject::WriteProperty\n" + " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {\n" + " qt_static_metacall(this, _c, _id, _a);\n" + " _id -= %d;\n }", cdef->propertyList.count()); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::QueryPropertyDesignable) {\n"); + if (needDesignable) { + fprintf(out, " bool *_b = reinterpret_cast(_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, " default: break;\n"); + 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(_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, " default: break;\n"); + 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(_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, " default: break;\n"); + 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(_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, " default: break;\n"); + 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(_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, " default: break;\n"); + 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"); +} + + +QMultiMap Generator::automaticPropertyMetaTypesHelper() +{ + QMultiMap automaticPropertyMetaTypes; + for (int i = 0; i < cdef->propertyList.size(); ++i) { + const QByteArray propertyType = cdef->propertyList.at(i).type; + if (registerableMetaType(propertyType) && !isBuiltinType(propertyType)) + automaticPropertyMetaTypes.insert(propertyType, i); + } + return automaticPropertyMetaTypes; +} + +QMap > Generator::methodsWithAutomaticTypesHelper(const QList &methodList) +{ + QMap > methodsWithAutomaticTypes; + for (int i = 0; i < methodList.size(); ++i) { + const FunctionDef &f = methodList.at(i); + for (int j = 0; j < f.arguments.count(); ++j) { + const QByteArray argType = f.arguments.at(j).normalizedType; + if (registerableMetaType(argType) && !isBuiltinType(argType)) + methodsWithAutomaticTypes[i].insert(argType, j); + } + } + return methodsWithAutomaticTypes; +} + +void Generator::generateStaticMetacall() +{ + fprintf(out, "void %s::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)\n{\n", + cdef->qualified.constData()); + + bool needElse = false; + bool isUsed_a = false; + + if (!cdef->constructorList.isEmpty()) { + 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->classname.constData(), cdef->classname.constData()); + const FunctionDef &f = cdef->constructorList.at(ctorindex); + int offset = 1; + + int argsCount = f.arguments.count(); + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (j) + fprintf(out, ","); + fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++); + } + if (f.isPrivateSignal) { + if (argsCount > 0) + fprintf(out, ", "); + fprintf(out, "%s", QByteArray("QPrivateSignal()").constData()); + } + fprintf(out, ");\n"); + fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n", + cdef->hasQGadget ? "void" : "QObject"); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + fprintf(out, " }"); + needElse = true; + isUsed_a = true; + } + + QList methodList; + methodList += cdef->signalList; + methodList += cdef->slotList; + methodList += cdef->methodList; + + if (!methodList.isEmpty()) { + if (needElse) + fprintf(out, " else "); + else + fprintf(out, " "); + fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " Q_UNUSED(_t)\n"); + fprintf(out, " switch (_id) {\n"); + for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { + const FunctionDef &f = methodList.at(methodindex); + Q_ASSERT(!f.normalizedType.isEmpty()); + fprintf(out, " case %d: ", methodindex); + + //---- + if (f.implementation) { + fprintf(out, f.implementation, methodindex); + fprintf(out, " break;\n"); + continue; + } + //---- + + if (f.normalizedType != "void") + fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); + fprintf(out, "_t->"); + if (f.inPrivateClass.size()) + fprintf(out, "%s->", f.inPrivateClass.constData()); + fprintf(out, "%s(", f.name.constData()); + int offset = 1; + + int argsCount = f.arguments.count(); + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (j) + fprintf(out, ","); + fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); + isUsed_a = true; + } + if (f.isPrivateSignal) { + if (argsCount > 0) + fprintf(out, ", "); + fprintf(out, "%s", "QPrivateSignal()"); + } + fprintf(out, ");"); + if (f.normalizedType != "void") { + fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", + noRef(f.normalizedType).constData()); + isUsed_a = true; + } + fprintf(out, " break;\n"); + } + fprintf(out, " default: ;\n"); + fprintf(out, " }\n"); + fprintf(out, " }"); + needElse = true; + + QMap > methodsWithAutomaticTypes = methodsWithAutomaticTypesHelper(methodList); + + if (!methodsWithAutomaticTypes.isEmpty()) { + fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); + fprintf(out, " switch (_id) {\n"); + fprintf(out, " default: *reinterpret_cast(_a[0]) = -1; break;\n"); + QMap >::const_iterator it = methodsWithAutomaticTypes.constBegin(); + const QMap >::const_iterator end = methodsWithAutomaticTypes.constEnd(); + for ( ; it != end; ++it) { + fprintf(out, " case %d:\n", it.key()); + fprintf(out, " switch (*reinterpret_cast(_a[1])) {\n"); + fprintf(out, " default: *reinterpret_cast(_a[0]) = -1; break;\n"); + foreach (const QByteArray &key, it->uniqueKeys()) { + foreach (int argumentID, it->values(key)) + fprintf(out, " case %d:\n", argumentID); + fprintf(out, " *reinterpret_cast(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData()); + } + fprintf(out, " }\n"); + fprintf(out, " break;\n"); + } + fprintf(out, " }\n"); + fprintf(out, " }"); + isUsed_a = true; + } + + } + if (!cdef->signalList.isEmpty()) { + Q_ASSERT(needElse); // if there is signal, there was method. + fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n"); + fprintf(out, " int *result = reinterpret_cast(_a[0]);\n"); + fprintf(out, " void **func = reinterpret_cast(_a[1]);\n"); + bool anythingUsed = false; + for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { + const FunctionDef &f = cdef->signalList.at(methodindex); + if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.implementation) + continue; + anythingUsed = true; + fprintf(out, " {\n"); + fprintf(out, " typedef %s (%s::*_t)(",f.type.rawName.constData() , cdef->classname.constData()); + + int argsCount = f.arguments.count(); + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (j) + fprintf(out, ", "); + fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData()); + } + if (f.isPrivateSignal) { + if (argsCount > 0) + fprintf(out, ", "); + fprintf(out, "%s", "QPrivateSignal"); + } + if (f.isConst) + fprintf(out, ") const;\n"); + else + fprintf(out, ");\n"); + fprintf(out, " if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&%s::%s)) {\n", + cdef->classname.constData(), f.name.constData()); + fprintf(out, " *result = %d;\n", methodindex); + fprintf(out, " }\n }\n"); + } + if (!anythingUsed) + fprintf(out, " Q_UNUSED(result);\n Q_UNUSED(func);\n"); + fprintf(out, " }"); + needElse = true; + } + + QMultiMap automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper(); + + if (!automaticPropertyMetaTypes.isEmpty()) { + if (needElse) + fprintf(out, " else "); + else + fprintf(out, " "); + fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n"); + fprintf(out, " switch (_id) {\n"); + fprintf(out, " default: *reinterpret_cast(_a[0]) = -1; break;\n"); + foreach (const QByteArray &key, automaticPropertyMetaTypes.uniqueKeys()) { + foreach (int propertyID, automaticPropertyMetaTypes.values(key)) + fprintf(out, " case %d:\n", propertyID); + fprintf(out, " *reinterpret_cast(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData()); + } + fprintf(out, " }\n"); + fprintf(out, " }\n"); + isUsed_a = true; + needElse = true; + } + + if (!cdef->propertyList.empty()) { + bool needGet = false; + bool needTempVarForGet = false; + bool needSet = false; + bool needReset = false; + for (int i = 0; i < cdef->propertyList.size(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + needGet |= !p.read.isEmpty() || !p.member.isEmpty(); + if (!p.read.isEmpty() || !p.member.isEmpty()) + needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec + && p.gspec != PropertyDef::ReferenceSpec); + + needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); + needReset |= !p.reset.isEmpty(); + } + fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); + + if (needElse) + fprintf(out, "else "); + fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); + if (needGet) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " Q_UNUSED(_t)\n"); + 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() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + if (p.gspec == PropertyDef::PointerSpec) + fprintf(out, " case %d: _a[0] = const_cast(reinterpret_cast(%s%s())); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (p.gspec == PropertyDef::ReferenceSpec) + fprintf(out, " case %d: _a[0] = const_cast(reinterpret_cast(&%s%s())); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (cdef->enumDeclarations.value(p.type, false)) + fprintf(out, " case %d: *reinterpret_cast(_v) = QFlag(%s%s()); break;\n", + propindex, prefix.constData(), p.read.constData()); + else if (!p.read.isEmpty()) + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", + propindex, p.type.constData(), 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.member.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n"); + + if (needSet) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " Q_UNUSED(_t)\n"); + 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.constant) + continue; + if (p.write.isEmpty() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + if (cdef->enumDeclarations.value(p.type, false)) { + fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast(_v))); break;\n", + propindex, prefix.constData(), p.write.constData()); + } else if (!p.write.isEmpty()) { + fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", + propindex, prefix.constData(), p.write.constData(), p.type.constData()); + } else { + fprintf(out, " case %d:\n", propindex); + fprintf(out, " if (%s%s != *reinterpret_cast< %s*>(_v)) {\n", + prefix.constData(), p.member.constData(), p.type.constData()); + fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n", + prefix.constData(), p.member.constData(), p.type.constData()); + if (!p.notify.isEmpty() && p.notifyId != -1) { + const FunctionDef &f = cdef->signalList.at(p.notifyId); + if (f.arguments.size() == 0) + fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData()); + else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type) + fprintf(out, " Q_EMIT _t->%s(%s%s);\n", + p.notify.constData(), prefix.constData(), p.member.constData()); + } + fprintf(out, " }\n"); + fprintf(out, " break;\n"); + } + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n"); + if (needReset) { + if (cdef->hasQObject) { +#ifndef QT_NO_DEBUG + fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); +#endif + fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } else { + fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + } + fprintf(out, " Q_UNUSED(_t)\n"); + 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 = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + fprintf(out, " case %d: %s%s; break;\n", + propindex, prefix.constData(), p.reset.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + fprintf(out, " }"); + fprintf(out, "\n#endif // QT_NO_PROPERTIES"); + needElse = true; + } + + if (needElse) + fprintf(out, "\n"); + + if (methodList.isEmpty()) { + fprintf(out, " Q_UNUSED(_o);\n"); + if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) { + fprintf(out, " Q_UNUSED(_id);\n"); + fprintf(out, " Q_UNUSED(_c);\n"); + } + } + if (!isUsed_a) + fprintf(out, " Q_UNUSED(_a);\n"); + + fprintf(out, "}\n\n"); +} + +void Generator::generateSignal(FunctionDef *def,int index) +{ + if (def->wasCloned || def->isAbstract || def->implementation) + 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"; + } + + Q_ASSERT(!def->normalizedType.isEmpty()); + if (def->arguments.isEmpty() && def->normalizedType == "void") { + if (def->isPrivateSignal) + fprintf(out, "QPrivateSignal"); + + fprintf(out, ")%s\n{\n" + " QMetaObject::activate(%s, &staticMetaObject, %d, Q_NULLPTR);\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()); + } + if (def->isPrivateSignal) { + if (!def->arguments.isEmpty()) + fprintf(out, ", "); + fprintf(out, "QPrivateSignal"); + } + + fprintf(out, ")%s\n{\n", constQualifier); + if (def->type.name.size() && def->normalizedType != "void") { + QByteArray returnType = noRef(def->normalizedType); + if (returnType.endsWith('*')) { + fprintf(out, " %s _t0 = 0;\n", returnType.constData()); + } else { + fprintf(out, " %s _t0 = %s();\n", returnType.constData(), returnType.constData()); + } + } + + fprintf(out, " void *_a[] = { "); + if (def->normalizedType == "void") { + fprintf(out, "Q_NULLPTR"); + } else { + if (def->returnTypeIsVolatile) + fprintf(out, "const_cast(reinterpret_cast(&_t0))"); + else + fprintf(out, "const_cast(reinterpret_cast(&_t0))"); + } + int i; + for (i = 1; i < offset; ++i) + if (def->arguments.at(i - 1).type.isVolatile) + fprintf(out, ", const_cast(reinterpret_cast(&_t%d))", i); + else + fprintf(out, ", const_cast(reinterpret_cast(&_t%d))", i); + fprintf(out, " };\n"); + fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); + if (def->normalizedType != "void") + fprintf(out, " return _t0;\n"); + fprintf(out, "}\n"); +} + +#if 0 +static void writePluginMetaData(FILE *out, const QJsonObject &data) +{ + const QJsonDocument doc(data); + + fputs("\nQT_PLUGIN_METADATA_SECTION\n" + "static const unsigned char qt_pluginMetaData[] = {\n" + " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',\n ", out); +#if 0 + fprintf(out, "\"%s\";\n", doc.toJson().constData()); +#else + const QByteArray binary = doc.toBinaryData(); + const int last = binary.size() - 1; + for (int i = 0; i < last; ++i) { + uchar c = (uchar)binary.at(i); + if (c < 0x20 || c >= 0x7f) + fprintf(out, " 0x%02x,", c); + else if (c == '\'' || c == '\\') + fprintf(out, " '\\%c',", c); + else + fprintf(out, " '%c', ", c); + if (!((i + 1) % 8)) + fputs("\n ", out); + } + fprintf(out, " 0x%02x\n};\n", (uchar)binary.at(last)); +#endif +} + +void Generator::generatePluginMetaData() +{ + if (cdef->pluginData.iid.isEmpty()) + return; + + // Write plugin meta data #ifdefed QT_NO_DEBUG with debug=false, + // true, respectively. + + QJsonObject data; + const QString debugKey = QStringLiteral("debug"); + data.insert(QStringLiteral("IID"), QLatin1String(cdef->pluginData.iid.constData())); + data.insert(QStringLiteral("className"), QLatin1String(cdef->classname.constData())); + data.insert(QStringLiteral("version"), (int)QT_VERSION); + data.insert(debugKey, QJsonValue(false)); + data.insert(QStringLiteral("MetaData"), cdef->pluginData.metaData.object()); + + // Add -M args from the command line: + foreach (const QString &key, cdef->pluginData.metaArgs.keys()) + data.insert(key, cdef->pluginData.metaArgs.value(key)); + + fputs("\nQT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;\n\n" + "#ifdef QT_NO_DEBUG\n", out); + writePluginMetaData(out, data); + + fputs("\n#else // QT_NO_DEBUG\n", out); + + data.remove(debugKey); + data.insert(debugKey, QJsonValue(true)); + writePluginMetaData(out, data); + + fputs("#endif // QT_NO_DEBUG\n\n", out); + + // 'Use' all namespaces. + int pos = cdef->qualified.indexOf("::"); + for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) ) + fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData()); + fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n", + cdef->qualified.constData(), cdef->classname.constData()); +} +#endif + +QT_END_NAMESPACE diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h new file mode 100644 index 0000000..6388fe6 --- /dev/null +++ b/tools/qscxmlc/generator.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include "moc.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Generator +{ + QTextStream &out; + ClassDef *cdef; + QVector meta_data; +public: + Generator(ClassDef *classDef, const QList &metaTypes, const QHash &knownQObjectClasses, const QHash &knownGadgets, QTextStream &outfile); + void generateCode(); +private: + bool registerableMetaType(const QByteArray &propertyType); + void registerClassInfoStrings(); + void generateClassInfos(); + void registerFunctionStrings(const QList &list); + void generateFunctions(const QList &list, const char *functype, int type, int ¶msIndex); + void generateFunctionRevisions(const QList& list, const char *functype); + void generateFunctionParameters(const QList &list, const char *functype); + void generateTypeInfo(const QByteArray &typeName, bool allowEmptyName = false); + void registerEnumStrings(); + void generateEnums(int index); + void registerPropertyStrings(); + void generateProperties(); + void generateMetacall(); + void generateStaticMetacall(); + void generateSignal(FunctionDef *def, int index); +// void generatePluginMetaData(); + QMultiMap automaticPropertyMetaTypesHelper(); + QMap > methodsWithAutomaticTypesHelper(const QList &methodList); + + void strreg(const QByteArray &); // registers a string + int stridx(const QByteArray &); // returns a string's id + QList strings; + QByteArray purestSuperClass; + QList metaTypes; + QHash knownQObjectClasses; + QHash knownGadgets; +}; + +QT_END_NAMESPACE + +#endif // GENERATOR_H diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h new file mode 100644 index 0000000..5149454 --- /dev/null +++ b/tools/qscxmlc/moc.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOC_H +#define MOC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct QMetaObject; + +struct Type +{ + enum ReferenceType { NoReference, Reference, RValueReference, Pointer }; + + inline Type() : isVolatile(false), isScoped(false), /*firstToken(NOTOKEN), */referenceType(NoReference) {} + inline explicit Type(const QByteArray &_name) + : name(_name), rawName(name), isVolatile(false), isScoped(false), /*firstToken(NOTOKEN),*/ referenceType(NoReference) {} + QByteArray name; + //When used as a return type, the type name may be modified to remove the references. + // rawName is the type as found in the function signature + QByteArray rawName; + uint isVolatile : 1; + uint isScoped : 1; +// Token firstToken; + ReferenceType referenceType; +}; + +struct EnumDef +{ + QByteArray name; + QList values; + bool isEnumClass; // c++11 enum class + EnumDef() : isEnumClass(false) {} +}; + +struct ArgumentDef +{ + ArgumentDef() : isDefault(false) {} + Type type; + QByteArray rightType, normalizedType, name; + QByteArray typeNameForCast; // type name to be used in cast from void * in metacall + bool isDefault; +}; + +struct FunctionDef +{ + FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), isStatic(false), + inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), + isScriptable(false), isSlot(false), isSignal(false), isPrivateSignal(false), + isConstructor(false), isDestructor(false), isAbstract(false), revision(0), implementation(0) {} + Type type; + QByteArray normalizedType; + QByteArray tag; + QByteArray name; + bool returnTypeIsVolatile; + + QList arguments; + + enum Access { Private, Protected, Public }; + Access access; + bool isConst; + bool isVirtual; + bool isStatic; + bool inlineCode; + bool wasCloned; + + QByteArray inPrivateClass; + bool isCompat; + bool isInvokable; + bool isScriptable; + bool isSlot; + bool isSignal; + bool isPrivateSignal; + bool isConstructor; + bool isDestructor; + bool isAbstract; + + int revision; + + const char *implementation; +}; + +struct PropertyDef +{ + PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){} + QByteArray name, type, member, read, write, reset, designable, scriptable, editable, stored, user, notify, inPrivateClass; + int notifyId; + bool constant; + bool final; + enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; + Specification gspec; + bool stdCppSet() const { + QByteArray s("set"); + s += toupper(name[0]); + s += name.mid(1); + return (s == write); + } + int revision; +}; + + +struct ClassInfoDef +{ + QByteArray name; + QByteArray value; +}; + +struct ClassDef { + ClassDef(): + hasQObject(false), hasQGadget(false), notifyableProperties(0) + , revisionedMethods(0), revisionedProperties(0), begin(0), end(0){} + QByteArray classname; + QByteArray qualified; + QList > superclassList; + + struct Interface + { + inline explicit Interface(const QByteArray &_className) + : className(_className) {} + QByteArray className; + QByteArray interfaceId; + }; + QList >interfaceList; + + bool hasQObject; + bool hasQGadget; + + struct PluginData { + QByteArray iid; + QMap metaArgs; + QJsonDocument metaData; + } pluginData; + + QList constructorList; + QList signalList, slotList, methodList, publicList; + int notifyableProperties; + QList propertyList; + QList classInfoList; + QMap enumDeclarations; + QList enumList; + QMap flagAliases; + int revisionedMethods; + int revisionedProperties; + + int begin; + int end; +}; + +struct NamespaceDef { + QByteArray name; + int begin; + int end; +}; + +inline QByteArray noRef(const QByteArray &type) +{ + if (type.endsWith('&')) { + if (type.endsWith("&&")) + return type.left(type.length()-2); + return type.left(type.length()-1); + } + return type; +} + +QT_END_NAMESPACE + +#endif // MOC_H diff --git a/tools/qscxmlc/outputrevision.h b/tools/qscxmlc/outputrevision.h new file mode 100644 index 0000000..da8cd09 --- /dev/null +++ b/tools/qscxmlc/outputrevision.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OUTPUTREVISION_H +#define OUTPUTREVISION_H + +// if the output revision changes, you MUST change it in qobjectdefs.h too +enum { mocOutputRevision = 67 }; // moc format output revision + +#endif // OUTPUTREVISION_H diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro index 196af79..a7c3c45 100644 --- a/tools/qscxmlc/qscxmlc.pro +++ b/tools/qscxmlc/qscxmlc.pro @@ -2,15 +2,17 @@ option(host_build) TARGET = qscxmlc CONFIG += console c++11 -QT = core +QT = core-private DEFINES += BUILD_QSCXMLC SOURCES += \ + generator.cpp \ qscxmlc.cpp \ scxmlcppdumper.cpp HEADERS += \ + moc.h generator.h outputrevision.h utils.h \ scxmlcppdumper.h HEADERS += \ diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index 3ccfb0e..26d497b 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -32,6 +32,9 @@ #include #include #include +#include + +#include "generator.h" QT_BEGIN_NAMESPACE struct StringListDumper { @@ -98,9 +101,9 @@ struct ClassDump { Method init; Method initDataModel; StringListDumper dataMethods; + StringListDumper classMethods; Method constructor; Method destructor; - StringListDumper properties; StringListDumper signalMethods; QList publicMethods; QList protectedMethods; @@ -112,6 +115,8 @@ struct ClassDump { ClassDump() : needsEventFilter(false) {} + + QByteArray metaData; }; namespace { @@ -170,7 +175,6 @@ static QString toHex(const QString &str) static const char *headerStart = "#include \n" - "#include \n" "#include \n" "#include \n" "\n"; @@ -216,6 +220,7 @@ public: addSubStateMachineProperties(doc); addEvents(); + generateMetaObject(); generateTables(); } @@ -318,11 +323,12 @@ protected: QString name = mangledName(node); QString stateName = QStringLiteral("state_") + name; // Property stuff: - clazz.properties << QStringLiteral("Q_PROPERTY(QAbstractState *%1 READ %1() CONSTANT)").arg(name); - Method getter(QStringLiteral("QAbstractState *%1() const").arg(name)); - getter.impl << QStringLiteral("QAbstractState *%2::%1() const").arg(name) - << QStringLiteral("{ return &data->%1; }").arg(stateName); - clazz.publicMethods << getter; + if (m_qtMode) { + Method getter(QStringLiteral("bool %1() const").arg(name)); + getter.impl << QStringLiteral("bool %2::%1() const").arg(name) + << QStringLiteral("{ return data->%1.active(); }").arg(stateName); + clazz.publicMethods << getter; + } // Declaration: if (node->type == State::Final) { @@ -345,6 +351,16 @@ protected: if (node->type == State::Parallel) { clazz.init.impl << stateName + QStringLiteral(".setChildMode(QState::ParallelStates);"); } + if (!node->id.isEmpty()) { + clazz.init.impl << QStringLiteral("QObject::connect(&") + + stateName + + QStringLiteral(", SIGNAL(activeChanged(bool)), &stateMachine, SIGNAL(") + + node->id + + QStringLiteral("Changed(bool)));"); + } + + m_stateNames.append(node->id); + m_stateFieldNames.append(stateName); // visit the kids: m_parents.append(node); @@ -417,7 +433,13 @@ protected: bool visit(Transition *node) Q_DECL_OVERRIDE { const QString tName = transitionName(node); - m_knownEvents.unite(node->events.toSet()); + if (m_qtMode) { + foreach (const QString &event, node->events) { + if (isValidCppIdentifier(event)) { + m_knownEvents.insert(event); + } + } + } // Declaration: clazz.classFields << QStringLiteral("QScxmlTransition ") + tName + QLatin1Char(';'); @@ -541,6 +563,7 @@ protected: if (m_qtMode && node->type == QStringLiteral("qt:signal")) { if (!m_signals.contains(node->event)) { m_signals.insert(node->event); + m_signalNames.append(node->event); clazz.signalMethods << QStringLiteral("void %1(const QVariant &data);").arg(node->event); } } @@ -637,33 +660,29 @@ private: void addSubStateMachineProperties(ScxmlDocument *doc) { - QStringList serviceProps; foreach (ScxmlDocument *subDocs, doc->allSubDocuments) { QString name = subDocs->root->name; if (name.isEmpty()) continue; + auto mangledName = CppDumper::mangleId(name); + auto qualifiedName = namespacePrefix + mangledName; + if (m_serviceProps.contains(qMakePair(mangledName, qualifiedName))) + continue; + m_serviceProps.append(qMakePair(mangledName, qualifiedName)); + clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, mangledName); + clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(mangledName); - serviceProps.append(name); - clazz.classFields << QStringLiteral("%1%2 *%2;").arg(namespacePrefix, name); - clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(name); - clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%2 READ %2() NOTIFY %2Changed())").arg(namespacePrefix, name); - Method getter(QStringLiteral("%1%2 *%2() const").arg(namespacePrefix, name)); - getter.impl << QStringLiteral("%1%2 *%3::%2() const").arg(namespacePrefix, name, clazz.className) - << QStringLiteral("{ return data->%1; }").arg(name); - clazz.publicMethods << getter; - clazz.signalMethods << QStringLiteral("void %1Changed();").arg(name); - } - - if (!serviceProps.isEmpty()) { - Method reg(QStringLiteral("void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE Q_DECL_FINAL")); - reg.impl << QStringLiteral("void %1::setService(const QString &id, QScxmlInvokableService *service) {").arg(clazz.className); - foreach (const QString &prop, serviceProps) { - reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %2Changed)") - .arg(addString(prop)) - .arg(prop, namespacePrefix); + if (m_qtMode) { + Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, mangledName)); + getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName, clazz.className, mangledName) + << QStringLiteral("{ return data->%1; }").arg(mangledName); + clazz.publicMethods << getter; + clazz.signalMethods << QStringLiteral("void %1Changed(%2 *statemachine);").arg(mangledName, qualifiedName); } - reg.impl << QStringLiteral("}"); - clazz.protectedMethods.append(reg); + + clazz.dataMethods << QStringLiteral("%1 *machine_%2() const").arg(qualifiedName, mangledName) + << QStringLiteral("{ return %1; }").arg(name) + << QString(); } } @@ -689,17 +708,17 @@ private: } } - if (!m_signals.isEmpty()) { + if (!m_signalNames.isEmpty()) { clazz.needsEventFilter = true; clazz.init.impl << QStringLiteral("stateMachine.setScxmlEventFilter(this);"); auto &dm = clazz.dataMethods; dm << QStringLiteral("bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) Q_DECL_OVERRIDE {"); if (m_qtMode) { dm << QStringLiteral(" if (event->originType() != QStringLiteral(\"qt:signal\")) { return true; }") - << QStringLiteral(" %1 *m = qobject_cast<%1 *>(stateMachine);").arg(clazz.className); - foreach (const QString &s, m_signals) { + << QStringLiteral(" %1 *m = static_cast<%1 *>(stateMachine);").arg(clazz.className); + foreach (const QString &signalName, m_signalNames) { dm << QStringLiteral(" if (event->name() == %1) { emit m->%2(event->data()); return false; }") - .arg(qba(s), CppDumper::mangleId(s)); + .arg(qba(signalName), CppDumper::mangleId(signalName)); } } dm << QStringLiteral(" return true;") @@ -995,7 +1014,7 @@ private: for (int i = 0, ei = strings.size(); i < ei; ++i) { QString s = strings.at(i); QString comment = cEscape(s); - t << QStringLiteral("QT_UNICODE_LITERAL_II(\"%1\") // %2").arg(toHex(s) + QStringLiteral("\\x00"), comment); + t << QStringLiteral("QT_UNICODE_LITERAL_II(\"%1\") // %3: %2").arg(toHex(s) + QStringLiteral("\\x00"), comment, QString::number(i)); } } t << QStringLiteral("};") << QStringLiteral(""); @@ -1021,6 +1040,159 @@ private: } } + void generateMetaObject() + { + ClassDef classDef; + classDef.classname = clazz.className.toUtf8(); + classDef.qualified = classDef.classname; + classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public); + classDef.hasQObject = true; + + // Event signals: + foreach (const QString &signalName, m_signalNames) { + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = signalName.toUtf8(); + signal.access = FunctionDef::Public; + signal.isSignal = true; + + ArgumentDef arg; + arg.type.name = "const QVariant &"; + arg.type.rawName = arg.type.name; + arg.normalizedType = "QVariant"; + arg.name = "data"; + arg.typeNameForCast = arg.normalizedType + "*"; + signal.arguments << arg; + + classDef.signalList << signal; + } + + // stateNames: + foreach (const QString &stateName, m_stateNames) { + if (stateName.isEmpty()) + continue; + + QByteArray mangledStateName = CppDumper::mangleId(stateName).toUtf8(); + + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = mangledStateName + "Changed"; + signal.access = FunctionDef::Private; + signal.isSignal = true; + if (!m_qtMode) { + signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; + } else { + clazz.signalMethods << QStringLiteral("void %1Changed(bool active);").arg(stateName); + } + ArgumentDef arg; + arg.type.name = "bool"; + arg.type.rawName = arg.type.name; + arg.normalizedType = arg.type.name; + arg.name = "active"; + arg.typeNameForCast = arg.type.name + "*"; + signal.arguments << arg; + classDef.signalList << signal; + + ++classDef.notifyableProperties; + PropertyDef prop; + prop.name = stateName.toUtf8(); + prop.type = "bool"; + prop.read = "data->state_" + mangledStateName + ".active"; + prop.notify = mangledStateName + "Changed"; + prop.notifyId = classDef.signalList.size() - 1; + prop.gspec = PropertyDef::ValueSpec; + prop.scriptable = "true"; + classDef.propertyList << prop; + } + + // event slots: + foreach (const QString &eventName, m_knownEvents) { + FunctionDef slot; + slot.type.name = "void"; + slot.type.rawName = slot.type.name; + slot.normalizedType = slot.type.name; + slot.name = eventName.toUtf8(); + slot.access = FunctionDef::Public; + slot.isSlot = true; + + classDef.slotList << slot; + + ArgumentDef arg; + arg.type.name = "const QVariant &"; + arg.type.rawName = arg.type.name; + arg.normalizedType = "QVariant"; + arg.name = "data"; + arg.typeNameForCast = arg.normalizedType + "*"; + slot.arguments << arg; + + classDef.slotList << slot; + } + + // sub-statemachines: + QHash knownQObjectClasses; + knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray()); + Method reg(QStringLiteral("void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE Q_DECL_FINAL")); + reg.impl << QStringLiteral("void %1::setService(const QString &id, QScxmlInvokableService *service) {").arg(clazz.className); + if (m_serviceProps.isEmpty()) { + reg.impl << QStringLiteral(" Q_UNUSED(id);") + << QStringLiteral(" Q_UNUSED(service);"); + } + for (const auto &service : m_serviceProps) { + auto serviceName = service.first; + QString fqServiceClass = service.second; + QByteArray serviceClass = fqServiceClass.toUtf8(); + knownQObjectClasses.insert(serviceClass, ""); + + reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %4)") + .arg(addString(serviceName)) + .arg(serviceName, namespacePrefix).arg(classDef.signalList.size()); + + QByteArray mangledServiceName = CppDumper::mangleId(serviceName).toUtf8(); + + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = mangledServiceName + "Changed"; + signal.access = FunctionDef::Private; + signal.isSignal = true; + if (!m_qtMode) { + signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; + } + ArgumentDef arg; + arg.type.name = serviceClass + " *"; + arg.type.rawName = arg.type.name; + arg.type.referenceType = Type::Pointer; + arg.normalizedType = serviceClass + "*(*)"; + arg.name = "statemachine"; + arg.typeNameForCast = arg.type.name + "*"; + signal.arguments << arg; + classDef.signalList << signal; + + ++classDef.notifyableProperties; + PropertyDef prop; + prop.name = serviceName.toUtf8(); + prop.type = serviceClass + "*"; + prop.read = "data->machine_" + mangledServiceName; + prop.notify = mangledServiceName + "Changed"; + prop.notifyId = classDef.signalList.size() - 1; + prop.gspec = PropertyDef::ValueSpec; + prop.scriptable = "true"; + classDef.propertyList << prop; + } + reg.impl << QStringLiteral("}"); + clazz.protectedMethods.append(reg); + + QBuffer buf(&clazz.metaData); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + Generator(&classDef, QList(), knownQObjectClasses, QHash(), out).generateCode(); + } + QString qba(const QString &bytes) { return QStringLiteral("string(%1)").arg(addString(bytes)); @@ -1039,8 +1211,12 @@ private: TranslationUnit *translationUnit; QHash m_mangledNames; QVector m_parents; + QList> m_serviceProps; QSet m_knownEvents; QSet m_signals; + QStringList m_signalNames; + QStringList m_stateNames; + QStringList m_stateFieldNames; QString m_currentTransitionName; bool m_bindLate; bool m_qtMode; @@ -1085,7 +1261,7 @@ void CppDumper::dump(TranslationUnit *unit) writeImplEnd(); } -QString CppDumper::mangleId(const QString &id) +QString CppDumper::mangleId(const QString &id) // TODO: remove { QString mangled(id); mangled = mangled.replace(QLatin1Char('_'), QLatin1String("__")); @@ -1116,9 +1292,13 @@ void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList & void CppDumper::writeClass(const ClassDump &clazz) { h << l("class ") << clazz.className << QStringLiteral(": public QScxmlStateMachine\n{") << endl; - h << QLatin1String(" Q_OBJECT\n"); - clazz.properties.write(h, QStringLiteral(" "), QStringLiteral("\n")); - h << QLatin1String("\npublic:\n"); + h << QStringLiteral("public:") << endl + << QStringLiteral(" /* qmake ignore Q_OBJECT */") << endl + << QStringLiteral(" Q_OBJECT") << endl + ; + + h << endl + << QStringLiteral("public:") << endl; h << l(" ") << clazz.className << l("(QObject *parent = 0);") << endl; h << l(" ~") << clazz.className << "();" << endl; @@ -1203,9 +1383,12 @@ void CppDumper::writeImplBody(const ClassDump &clazz) } cpp << l(" {") << endl; - cpp << QStringLiteral(" Data(%1 &stateMachine)\n : stateMachine(stateMachine)").arg(clazz.className) << endl; + cpp << QStringLiteral(" Data(%1 &stateMachine)").arg(clazz.className) << endl + << QStringLiteral(" : stateMachine(stateMachine)") << endl; clazz.constructor.initializer.write(cpp, QStringLiteral(" , "), QStringLiteral("\n")); - cpp << l(" { init(); }") << endl; + cpp << l(" {") << endl; + clazz.constructor.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n")); + cpp << l(" }") << endl; cpp << endl; cpp << l(" void init() {\n"); @@ -1220,10 +1403,12 @@ void CppDumper::writeImplBody(const ClassDump &clazz) cpp << l("};") << endl << endl; + clazz.classMethods.write(cpp, QStringLiteral(""), QStringLiteral("\n")); + cpp << clazz.className << l("::") << clazz.className << l("(QObject *parent)") << endl << QStringLiteral(" : QScxmlStateMachine(parent)") << endl << QStringLiteral(" , data(new Data(*this))") << endl - << QStringLiteral("{ qRegisterMetaType<%1 *>(); qRegisterMetaType(); }").arg(clazz.className) << endl + << QStringLiteral("{ qRegisterMetaType<%1 *>(); data->init(); }").arg(clazz.className) << endl << endl; cpp << clazz.className << l("::~") << clazz.className << l("()") << endl << l("{ delete data; }") << endl @@ -1239,7 +1424,8 @@ void CppDumper::writeImplBody(const ClassDump &clazz) " fq *casted = machine ? dynamic_cast(machine->stateMachine()) : Q_NULLPTR; \\\n" " if (data->n != casted) { \\\n" " data->n = casted; \\\n" - " emit sig(); \\\n" + " void *_a[] = { Q_NULLPTR, const_cast(reinterpret_cast(&casted)) }; \\\n" + " QMetaObject::activate(this, &staticMetaObject, sig, _a); \\\n" " } \\\n" " return; \\\n" " }\n" @@ -1267,6 +1453,8 @@ void CppDumper::writeImplBody(const ClassDump &clazz) m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n")); } } + + cpp << endl << clazz.metaData; } void CppDumper::writeImplEnd() diff --git a/tools/qscxmlc/utils.h b/tools/qscxmlc/utils.h new file mode 100644 index 0000000..58cf924 --- /dev/null +++ b/tools/qscxmlc/utils.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UTILS_H +#define UTILS_H + +#include + +QT_BEGIN_NAMESPACE + +inline bool is_whitespace(char s) +{ + return (s == ' ' || s == '\t' || s == '\n'); +} + +inline bool is_space(char s) +{ + return (s == ' ' || s == '\t'); +} + +inline bool is_ident_start(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || s == '_' || s == '$' + ); +} + +inline bool is_ident_char(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || (s >= '0' && s <= '9') + || s == '_' || s == '$' + ); +} + +inline bool is_identifier(const char *s, int len) +{ + if (len < 1) + return false; + if (!is_ident_start(*s)) + return false; + for (int i = 1; i < len; ++i) + if (!is_ident_char(s[i])) + return false; + return true; +} + +inline bool is_digit_char(char s) +{ + return (s >= '0' && s <= '9'); +} + +inline bool is_octal_char(char s) +{ + return (s >= '0' && s <= '7'); +} + +inline bool is_hex_char(char s) +{ + return ((s >= 'a' && s <= 'f') + || (s >= 'A' && s <= 'F') + || (s >= '0' && s <= '9') + ); +} + +inline const char *skipQuote(const char *data) +{ + while (*data && (*data != '\"')) { + if (*data == '\\') { + ++data; + if (!*data) break; + } + ++data; + } + + if (*data) //Skip last quote + ++data; + return data; +} + +QT_END_NAMESPACE + +#endif // UTILS_H -- cgit v1.2.3