diff options
Diffstat (limited to 'generator/classgenerator.cpp')
-rw-r--r-- | generator/classgenerator.cpp | 1817 |
1 files changed, 1817 insertions, 0 deletions
diff --git a/generator/classgenerator.cpp b/generator/classgenerator.cpp new file mode 100644 index 0000000..d974b4c --- /dev/null +++ b/generator/classgenerator.cpp @@ -0,0 +1,1817 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt Script Generator project on Trolltech Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.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 GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "classgenerator.h" +#include "fileout.h" + +#include <QtCore/QDir> +#include <QtCore/QMetaType> + +#include <qdebug.h> + +#define GENERATOR_NO_PROTECTED_FUNCTIONS + +ClassGenerator::ClassGenerator(PriGenerator *pri, SetupGenerator *setup) : + priGenerator(pri), + setupGenerator(setup) +{} + +QString ClassGenerator::fileNameForClass(const AbstractMetaClass *meta_class) const +{ + return QString("qtscript_%1.cpp").arg(meta_class->name()); +} + +bool ClassGenerator::shouldGenerate(const AbstractMetaClass *meta_class) const +{ + uint cg = meta_class->typeEntry()->codeGeneration(); + return (cg & TypeEntry::GenerateCode) != 0; +} + +static QString normalizedType(const AbstractMetaType *type) +{ + QString str = QString::fromLatin1(QMetaObject::normalizedType(type->cppSignature().toLatin1())); + if (str.endsWith(QLatin1Char('&'))) + str.chop(1); + else if (str.startsWith("const ")) { + if (str.endsWith('*') || type->hasInstantiations() || type->typeEntry()->isValue()) + str.remove(0, 6); + } + if (str == QLatin1String("QBool")) // ### hack + str = QLatin1String("bool"); + return str; +} + +/*! + Returns true if the class \a meta_class inherits from QObject, + otherwise returns false. +*/ +static bool isQObjectBased(const AbstractMetaClass *meta_class) +{ + while (meta_class) { + if (meta_class->name() == QLatin1String("QObject")) + return true; + meta_class = meta_class->baseClass(); + } + return false; +} + +/*! + Returns true if any of the given \a enums has been declared with + Q_ENUMS. +*/ +static bool hasQEnums(const AbstractMetaEnumList &enums) +{ + for (int i = 0; i < enums.size(); ++i) { + if (enums.at(i)->hasQEnumsDeclaration()) + return true; + } + return false; +} + +/*! + Returns true if any of the given \a enums has a QFlags class + associated with it. +*/ +static bool hasFlags(const AbstractMetaEnumList &enums) +{ + for (int i = 0; i < enums.size(); ++i) { + FlagsTypeEntry *flags = enums.at(i)->typeEntry()->flags(); + if (flags) + return true; + } + return false; +} + +static bool isSequenceType(const AbstractMetaType *tp) +{ + return tp->isContainer() && (tp->instantiations().size() == 1); +} + +static AbstractMetaFunction *findDefaultConstructor(const AbstractMetaFunctionList &ctors) +{ + for (int i = 0; i < ctors.size(); ++i) { + if (ctors.at(i)->actualMinimumArgumentCount() == 0) + return ctors.at(i); + } + return 0; +} + +static AbstractMetaFunctionList findConstructors(const AbstractMetaClass *meta_class) +{ + return meta_class->queryFunctions(AbstractMetaClass::Constructors + | AbstractMetaClass::WasPublic + | AbstractMetaClass::NotRemovedFromTargetLang); +} + +/*! + Returns true if \a meta_class has a default constructor, false + otherwise. +*/ +bool hasDefaultConstructor(const AbstractMetaClass *meta_class) +{ + return findDefaultConstructor(findConstructors(meta_class)) != 0; +} + +/*! + Given the list of \a functions, creates a mapping from # of + arguments to list of functions. +*/ +static QMap<int, AbstractMetaFunctionList> createArgcToFunctionsMap( + const AbstractMetaFunctionList &functions) +{ + QMap<int, AbstractMetaFunctionList> result; + for (int i = 0; i < functions.size(); ++i) { + AbstractMetaFunction *func = functions.at(i); + int argc = func->arguments().size(); + for (int k = argc; k > 0; --k) { + if (func->argumentRemoved(k)) + --argc; + } + for (int j = func->actualMinimumArgumentCount(); j <= argc; ++j) + result[j].append(func); + } + return result; +} + +/*! + Returns the name of the QScriptValue function to use to test if + a value is of the given \a typeName, or an empty string if there + is no such function. +*/ +static QString builtinTypeTesterFunction(const QString &typeName) +{ + if (typeName == QLatin1String("QString")) + return QLatin1String("isString"); + else if (typeName == QLatin1String("double")) + return QLatin1String("isNumber"); + else if (typeName == QLatin1String("int")) + return QLatin1String("isNumber"); + else if (typeName == QLatin1String("uint")) + return QLatin1String("isNumber"); + else if (typeName == QLatin1String("bool")) + return QLatin1String("isBoolean"); + else if (typeName == QLatin1String("QVariant")) + return QLatin1String("isVariant"); +// else if (typeName == QLatin1String("QDateTime")) +// return QLatin1String("isDate"); + else if (typeName == QLatin1String("QRegExp")) + return QLatin1String("isRegExp"); + else if (typeName == QLatin1String("QObject*")) + return QLatin1String("isQObject"); + return QString(); +} + +/*! + Writes the code injections for the class \a meta_class that should + be injected at position \a pos. +*/ +static void writeInjectedCode(QTextStream &s, const AbstractMetaClass *meta_class, + CodeSnip::Position pos) +{ + CodeSnipList code_snips = meta_class->typeEntry()->codeSnips(); + foreach (const CodeSnip &cs, code_snips) { + if ((cs.language == TypeSystem::NativeCode) && (cs.position == pos)) { + s << cs.code() << endl; + } + } +} + +/*! + Writes the code injections for the function \a fun of the class \a + meta_class that should be injected at position \a pos. +*/ +static void writeInjectedCode(QTextStream &s, const AbstractMetaClass *meta_class, + const AbstractMetaFunction *fun, CodeSnip::Position pos) +{ + FunctionModificationList mods = fun->modifications(meta_class); + foreach (const FunctionModification &mod, mods) { + if (!mod.isCodeInjection()) + continue; + foreach (const CodeSnip &cs, mod.snips) { + if ((cs.language == TypeSystem::NativeCode) && (cs.position == pos)) { + s << cs.code() << endl; + } + } + } +} + +/*! + Writes a boolean expression that checks if the actual arguments are + compatible with what the function expects. This is used to resolve + ambiguous calls. +*/ +static void writeArgumentTypeTests(QTextStream &stream, const AbstractMetaFunction *fun, + const AbstractMetaArgumentList &arguments, int argc, int indent) +{ + QString indentStr(indent, QLatin1Char(' ')); + int j = 0; + for (int i = 0; i < argc; ++j) { + if (fun->argumentRemoved(j+1)) + continue; + if (i > 0) + stream << endl << indentStr << "&& "; + const AbstractMetaType *argType = 0; + QString typeName = fun->typeReplaced(j+1); + if (typeName.isEmpty()) { + AbstractMetaArgument *arg = arguments.at(j); + argType = arg->type(); + typeName = normalizedType(argType); + } + QString scriptArg = QString::fromLatin1("context->argument(%0)").arg(i); + if (argType && isSequenceType(argType)) { + stream << scriptArg << ".isArray()"; + } else { + QString tester = builtinTypeTesterFunction(typeName); + if (!tester.isEmpty()) { + stream << scriptArg << "." << tester << "()"; + } else if (typeName.endsWith('*')) { + stream << "qscriptvalue_cast<" << typeName << ">(" << scriptArg << ")"; + } else { + // typeid-based test + stream << "(qMetaTypeId<" << typeName; + if (typeName.endsWith(QLatin1Char('>'))) + stream << " "; + stream << ">() == " << scriptArg << ".toVariant().userType())"; + } + } + ++i; + } +} + +/*! + Returns the name of the QScriptValue function to use to convert a + value is of the given \a typeName, or an empty string if there is no + such function. +*/ +static QString builtinConversionFunction(const QString &typeName) +{ + if (typeName == QLatin1String("QString")) + return QLatin1String("toString"); + else if (typeName == QLatin1String("double")) + return QLatin1String("toNumber"); + else if (typeName == QLatin1String("int")) + return QLatin1String("toInt32"); + else if (typeName == QLatin1String("uint")) + return QLatin1String("toUInt32"); + else if (typeName == QLatin1String("bool")) + return QLatin1String("toBoolean"); + else if (typeName == QLatin1String("QVariant")) + return QLatin1String("toVariant"); + else if (typeName == QLatin1String("QDateTime")) + return QLatin1String("toDateTime"); + else if (typeName == QLatin1String("QRegExp")) + return QLatin1String("toRegExp"); + else if (typeName == QLatin1String("QObject*")) + return QLatin1String("toQObject"); + return QString(); +} + +/*! + Generates script arguments --> C++ types conversion, in preparation + for calling the native function we are binding. +*/ +static int writePrepareArguments(QTextStream &stream, const AbstractMetaFunction *fun, + const AbstractMetaArgumentList &arguments, + int scriptArgc, int indent) +{ + if (arguments.size() == 0) { + Q_ASSERT(scriptArgc == 0); + return 0; // nothing to do + } + QString indentStr(indent, QLatin1Char(' ')); + int j = 0; + for (int scriptArgIndex = 0; j < arguments.size(); ++j) { + const AbstractMetaArgument *arg = arguments.at(j); + bool isOptional = !arg->defaultValueExpression().isEmpty(); + if (isOptional && (scriptArgIndex == scriptArgc)) + break; + QString conv = fun->conversionRule(TypeSystem::NativeCode, j+1); + QString actualIn = QString::fromLatin1("context->argument(%0)").arg(scriptArgIndex); + QString actualOut = QString::fromLatin1("_q_arg%0").arg(j); + if (!conv.isEmpty()) { + // custom conversion + conv.replace(QString::fromLatin1("%in%"), actualIn); + conv.replace(QString::fromLatin1("%out%"), actualOut); + conv.replace(QString::fromLatin1("%this%"), QString::fromLatin1("_q_self")); + stream << conv; + } else { + const AbstractMetaType *argType = 0; + QString typeName = fun->typeReplaced(j+1); + if (typeName.isEmpty()) { + argType = arg->type(); + typeName = normalizedType(argType); + } + stream << indentStr << typeName << " " << actualOut; + QString converter; + // ### generalize the QSet check (we should check if the type has push_back()) + bool useToSequence = argType && isSequenceType(argType) && !argType->name().startsWith("Set"); + if (useToSequence) { + stream << ";" << endl; + stream << indentStr << "qScriptValueToSequence("; + } else { + stream << " = "; + converter = builtinConversionFunction(typeName); + if (converter.isEmpty()) { + // generic conversion + stream << "qscriptvalue_cast<" << typeName; + if (typeName.endsWith(QLatin1Char('>'))) + stream << " "; + stream << ">("; + } + } + stream << actualIn; + if (useToSequence) { + stream << ", " << actualOut << ")"; + } else { + if (converter.isEmpty()) { + stream << ")"; // close qscriptvalue_cast + } else { + stream << "." << converter << "()"; + } + } + stream << ";" << endl; + } + if (!fun->argumentRemoved(j+1)) + ++scriptArgIndex; + } + return j; +} + +/*! + Writes the arguments that are passed to the native function we are + binding. Those arguments must have been prepared already in variables + _q_arg0, _q_arg1, .. in the generated code. +*/ +static void writeArguments(QTextStream &stream, int count) +{ + for (int i = 0; i < count; ++i) { + if (i > 0) + stream << ", "; + stream << "_q_arg" << i; + } +} + +/*! + Writes a constructor call. +*/ +static void writeConstructorCallAndReturn(QTextStream &stream, const AbstractMetaFunction *fun, + int scriptArgc, const AbstractMetaClass *meta_class, + int indent) +{ + QString indentStr(indent, QLatin1Char(' ')); + + writeInjectedCode(stream, meta_class, fun, CodeSnip::Beginning); + + AbstractMetaArgumentList arguments = fun->arguments(); + Q_ASSERT(arguments.size() >= scriptArgc); + int nativeArgc = writePrepareArguments(stream, fun, arguments, scriptArgc, indent); + stream << indentStr; + if (meta_class->generateShellClass()) { + stream << "QtScriptShell_" << meta_class->name(); + } else { + stream << meta_class->qualifiedCppName(); + } + bool useNew = meta_class->typeEntry()->isObject() || !hasDefaultConstructor(meta_class); + if (useNew) + stream << "*"; + stream << " _q_cpp_result"; + if (useNew) { + stream << " = new "; + if (meta_class->generateShellClass()) + stream << "QtScriptShell_" << meta_class->name(); + else + stream << meta_class->qualifiedCppName(); + } + if (useNew || (nativeArgc != 0)) { + stream << "("; + writeArguments(stream, nativeArgc); + stream << ")"; + } + stream << ";" << endl; + + stream << indentStr << "QScriptValue _q_result = context->engine()->new"; + if (isQObjectBased(meta_class)) + stream << "QObject"; + else + stream << "Variant"; + stream << "(context->thisObject(), "; + if (!isQObjectBased(meta_class)) + stream << "qVariantFromValue("; + if (meta_class->generateShellClass()) { + stream << "(" << meta_class->qualifiedCppName(); + if (useNew) + stream << "*"; + stream << ")"; + } + stream << "_q_cpp_result"; + if (isQObjectBased(meta_class)) + stream << ", QScriptEngine::AutoOwnership"; + else + stream << ")"; + stream << ");" << endl; + if (meta_class->generateShellClass()) { + stream << indentStr << "_q_cpp_result"; + if (useNew) + stream << "->"; + else + stream << "."; + stream << "__qtscript_self = _q_result;" << endl; + } + + writeInjectedCode(stream, meta_class, fun, CodeSnip::End); + + stream << indentStr << "return _q_result;" << endl; +} + +/*! + Returns true if the given \a typeName has a QScriptValue constructor + we can use, false otherwise. +*/ +static bool hasScriptValueConstructor(const QString &typeName) +{ + return (typeName == QLatin1String("bool")) + || (typeName == QLatin1String("int")) + || (typeName == QLatin1String("uint")) + || (typeName == QLatin1String("double")) + || (typeName == QLatin1String("QString")); +} + +/*! + Writes a function call. +*/ +static void writeFunctionCallAndReturn(QTextStream &stream, const AbstractMetaFunction *fun, + int scriptArgc, const AbstractMetaClass *meta_class, + int indent) +{ + QString indentStr(indent, QLatin1Char(' ')); + AbstractMetaArgumentList arguments = fun->arguments(); + Q_ASSERT(arguments.size() >= scriptArgc); + + writeInjectedCode(stream, meta_class, fun, CodeSnip::Beginning); + + int nativeArgc = writePrepareArguments(stream, fun, arguments, scriptArgc, indent); + bool returnThisObject = fun->shouldReturnThisObject(); + bool ignoreReturnValue = returnThisObject || fun->shouldIgnoreReturnValue(); + stream << indentStr; + AbstractMetaType *retType = fun->type(); + bool constCastResult = false; + if (retType && !ignoreReturnValue) { + QString rsig = retType->cppSignature(); + QString typeName = normalizedType(retType); + stream << typeName << " _q_result = "; + constCastResult = rsig.endsWith('*') && rsig.startsWith("const "); + if (constCastResult) + stream << "const_cast<" << typeName << ">("; + } + + if (!fun->isStatic()) { + // ### the friendly check should be enough... + if (fun->isFriendly() + || ((fun->name() == QLatin1String("operator_equal")) + && ((meta_class->name() == QLatin1String("QPoint")) + || (meta_class->name() == QLatin1String("QPointF")) + || (meta_class->name() == QLatin1String("QRect")) + || (meta_class->name() == QLatin1String("QRectF")) + || (meta_class->name() == QLatin1String("QSize")) + || (meta_class->name() == QLatin1String("QSizeF"))))) { + stream << fun->originalName() << "("; + stream << "*_q_self, "; + } else { + stream << "_q_self->"; + stream << fun->originalName() << "("; + } + } else { + stream << meta_class->qualifiedCppName() << "::"; + stream << fun->originalName() << "("; + } + writeArguments(stream, nativeArgc); + if (constCastResult) + stream << ")"; + stream << ");" << endl; + + writeInjectedCode(stream, meta_class, fun, CodeSnip::End); + + // write return statement + stream << indentStr; + if (returnThisObject) { + stream << "return context->thisObject();"; + } else { + QString conv = fun->conversionRule(TypeSystem::NativeCode, 0); + if (!conv.isEmpty()) { + // custom conversion + conv.replace(QString::fromLatin1("%in%"), "_q_result"); + conv.replace(QString::fromLatin1("%out%"), "_q_convertedResult"); + stream << conv; + stream << "return qScriptValueFromValue(context->engine(), _q_convertedResult);"; + } else { + stream << "return "; + if (retType) { + if (isSequenceType(retType)) + stream << "qScriptValueFromSequence"; + else if (hasScriptValueConstructor(normalizedType(retType))) + stream << "QScriptValue"; + else + stream << "qScriptValueFromValue"; + stream << "(context->engine(), _q_result);"; + } else { + stream << "context->engine()->undefinedValue();"; + } + } + } + stream << endl; +} + +/*! + Returns true if the given function \a fun is operator>>() or + operator<<() that streams from/to a Q{Data,Text}Stream, false + otherwise. +*/ +static bool isSpecialStreamingOperator(const AbstractMetaFunction *fun) +{ + return ((fun->functionType() == AbstractMetaFunction::GlobalScopeFunction) + && (fun->arguments().size() == 1) + && (((fun->originalName() == "operator>>") && (fun->modifiedName() == "readFrom")) + || ((fun->originalName() == "operator<<") && (fun->modifiedName() == "writeTo")))); +} + +/*! + Generates code that uses Q{Data,Text}Stream operator>>() or + operator<<() to read/write an instance of meta_class. +*/ +static void writeStreamingOperatorCall(QTextStream &stream, const AbstractMetaFunction *fun, + const AbstractMetaClass */*meta_class*/, int indent) +{ + QString indentStr(indent, QLatin1Char(' ')); + QString streamClassName = fun->arguments().at(0)->type()->name(); + stream << indentStr << streamClassName << "* _q_arg0 = qscriptvalue_cast<" + << streamClassName << "*>(context->argument(0));" << endl; + stream << indentStr << "operator"; + if (fun->modifiedName() == "readFrom") + stream << ">>"; + else + stream << "<<"; + stream << "(*_q_arg0, *_q_self);" << endl; + stream << indentStr << "return context->engine()->undefinedValue();" << endl; +} + +/*! + Writes the constructor forwarding for \a meta_class. +*/ +static void writeConstructorForwarding(QTextStream &stream, + const AbstractMetaFunctionList &functions, + const AbstractMetaClass *meta_class) +{ +#if 0 + stream << "/** signatures:" << endl; + foreach (const AbstractMetaFunction *fun, functions) { + stream << " * " << fun->signature() << endl; + } + stream << " */" << endl; +#endif + + if (/*meta_class->isAbstract() ||*/ (functions.size() == 0)) { + stream << " return context->throwError(QScriptContext::TypeError," << endl + << " QString::fromLatin1(\"" << meta_class->name() + << " cannot be constructed\"));" << endl; + + } else { + stream << " if (context->thisObject().strictlyEquals(context->engine()->globalObject())) {" << endl + << " return context->throwError(QString::fromLatin1(\"" + << meta_class->name() << "(): Did you forget to construct with 'new'?\"));" << endl + << " }" << endl; + + writeInjectedCode(stream, meta_class, CodeSnip::Constructor); + + QMap<int, AbstractMetaFunctionList> argcToFunctions; + argcToFunctions = createArgcToFunctionsMap(functions); + + int argcMin = argcToFunctions.keys().first(); + int argcMax = argcToFunctions.keys().last(); + bool needElse = false; + for (int i = argcMin; i <= argcMax; ++i) { + AbstractMetaFunctionList funcs = argcToFunctions.value(i); + if (funcs.isEmpty()) + continue; + if (needElse) + stream << " else "; + else + stream << " "; + needElse = true; + stream << "if (context->argumentCount() == " << i << ") {" << endl; + if ((funcs.size() == 1) || (i == 0)) { + AbstractMetaFunction *fun = funcs.at(0); + const int indent = 8; + writeConstructorCallAndReturn(stream, fun, i, meta_class, indent); + } else { + // handle overloads + for (int j = 0; j < funcs.size(); ++j) { + AbstractMetaFunction *fun = funcs.at(j); + stream << " "; + if (j > 0) + stream << "} else "; + stream << "if ("; + AbstractMetaArgumentList arguments = fun->arguments(); + const int indent = 12; + writeArgumentTypeTests(stream, fun, arguments, i, indent); + stream << ") {" << endl; + writeConstructorCallAndReturn(stream, fun, i, meta_class, indent); + } + stream << " }" << endl; + } + stream << " }"; + } + stream << endl; +// writeThrowAmbiguityError(stream, meta_class, 0, signatures.toList()); + } +} + +/*! + Returns a list of enum \a values that are actually unique. + */ +QList<int> uniqueEnumValueIndexes(const AbstractMetaEnumValueList &values) +{ + QMap<int, int> map; + for (int i = 0; i < values.count(); ++i) { + AbstractMetaEnumValue *val = values.at(i); + if (!map.contains(val->value())) + map.insert(val->value(), i); + } + return map.values(); +} + +/*! + */ +static bool isContiguousEnum(const QList<int> &indexes, const AbstractMetaEnumValueList &values) +{ + if (indexes.isEmpty()) + return false; + int prev = values.at(indexes.at(0))->value(); + for (int i = 1; i < indexes.size(); ++i) { + int curr = values.at(indexes.at(i))->value(); + if (curr != prev + 1) + return false; + prev = curr; + } + return true; +} + +static void writeCreateEnumClassHelper(QTextStream &stream) +{ + stream << "static QScriptValue qtscript_create_enum_class_helper(" << endl + << " QScriptEngine *engine," << endl + << " QScriptEngine::FunctionSignature construct," << endl + << " QScriptEngine::FunctionSignature valueOf," << endl + << " QScriptEngine::FunctionSignature toString)" << endl + << "{" << endl + << " QScriptValue proto = engine->newObject();" << endl + << " proto.setProperty(QString::fromLatin1(\"valueOf\")," << endl + << " engine->newFunction(valueOf), QScriptValue::SkipInEnumeration);" << endl + << " proto.setProperty(QString::fromLatin1(\"toString\")," << endl + << " engine->newFunction(toString), QScriptValue::SkipInEnumeration);" << endl + << " return engine->newFunction(construct, proto, 1);" << endl + << "}" << endl << endl; +} + +static void writeCreateFlagsClassHelper(QTextStream &stream) +{ + stream << "static QScriptValue qtscript_create_flags_class_helper(" << endl + << " QScriptEngine *engine," << endl + << " QScriptEngine::FunctionSignature construct," << endl + << " QScriptEngine::FunctionSignature valueOf," << endl + << " QScriptEngine::FunctionSignature toString," << endl + << " QScriptEngine::FunctionSignature equals)" << endl + << "{" << endl + << " QScriptValue proto = engine->newObject();" << endl + << " proto.setProperty(QString::fromLatin1(\"valueOf\")," << endl + << " engine->newFunction(valueOf), QScriptValue::SkipInEnumeration);" << endl + << " proto.setProperty(QString::fromLatin1(\"toString\")," << endl + << " engine->newFunction(toString), QScriptValue::SkipInEnumeration);" << endl + << " proto.setProperty(QString::fromLatin1(\"equals\")," << endl + << " engine->newFunction(equals), QScriptValue::SkipInEnumeration);" << endl + << " return engine->newFunction(construct, proto);" << endl + << "}" << endl << endl; +} + +/*! + Writes the enum \a enom belonging to the class \a meta_class to the + given \a stream. + */ +static void writeEnumClass(QTextStream &stream, const AbstractMetaClass *meta_class, + const AbstractMetaEnum *enom) +{ + QString qualifiedEnumName = meta_class->qualifiedCppName() + "::" + enom->name(); + QString qtScriptEnumName = meta_class->name() + "_" + enom->name(); + + stream << "//" << endl; + stream << "// " << qualifiedEnumName << endl; + stream << "//" << endl << endl; + + // determine unique values (aliases will cause switch statement to not compile) + AbstractMetaEnumValueList values = enom->values(); + QList<int> uniqueIndexes = uniqueEnumValueIndexes(values); + + bool contiguous = isContiguousEnum(uniqueIndexes, values); + + // write arrays of values and keys + stream << "static const " << qualifiedEnumName + << " qtscript_" << qtScriptEnumName << "_values[] = {" << endl; + for (int i = 0; i < uniqueIndexes.size(); ++i) { + stream << " "; + if (i > 0) + stream << ", "; + stream << meta_class->qualifiedCppName() << "::" << values.at(uniqueIndexes.at(i))->name() << endl; + } + stream << "};" << endl << endl; + stream << "static const char * const qtscript_" << qtScriptEnumName << "_keys[] = {" << endl; + for (int i = 0; i < uniqueIndexes.size(); ++i) { + stream << " "; + if (i > 0) + stream << ", "; + stream << "\"" << values.at(uniqueIndexes.at(i))->name() << "\"" << endl; + } + stream << "};" << endl << endl; + + // write toString helper + stream << "static QString qtscript_" + << qtScriptEnumName << "_toStringHelper" + << "(" << qualifiedEnumName << " value)" << endl; + stream << "{" << endl; + if (enom->hasQEnumsDeclaration() && (meta_class->qualifiedCppName() != "QTransform")) { + stream << " const QMetaObject *meta = qtscript_" << meta_class->name() << "_metaObject();" << endl; + stream << " int idx = meta->indexOfEnumerator(\"" << enom->name() << "\");" << endl; + stream << " Q_ASSERT(idx != -1);" << endl; + stream << " QMetaEnum menum = meta->enumerator(idx);" << endl; + stream << " return QString::fromLatin1(menum.valueToKey(value));" << endl; + } else { + if (contiguous) { + stream << " if ((value >= " << meta_class->qualifiedCppName() + << "::" << values.at(uniqueIndexes.first())->name() << ")" + << " && (value <= " << meta_class->qualifiedCppName() + << "::" << values.at(uniqueIndexes.last())->name() << "))" << endl + << " return qtscript_" << qtScriptEnumName << "_keys[static_cast<int>(value)];" << endl; + } else { + stream << " for (int i = 0; i < " << uniqueIndexes.size() << "; ++i) {" << endl + << " if (qtscript_" << qtScriptEnumName << "_values[i] == value)" << endl + << " return QString::fromLatin1(qtscript_" << qtScriptEnumName << "_keys[i]);" << endl + << " }" << endl; + } + stream << " return QString();" << endl; + } + stream << "}" << endl << endl; + + // write QScriptValue <--> C++ conversion functions + stream << "static QScriptValue qtscript_" + << qtScriptEnumName << "_toScriptValue(" + << "QScriptEngine *engine, const " << qualifiedEnumName << " &value)" << endl + << "{" << endl + << " QScriptValue clazz = engine->globalObject().property(QString::fromLatin1(\"" + << meta_class->name() << "\"));" << endl +// << " QScriptValue enumClazz = clazz.property(QString::fromLatin1(\"" +// << enom->name() << "\"));" << endl + << " return clazz.property(qtscript_" << qtScriptEnumName << "_toStringHelper(value));" << endl + << "}" << endl << endl; + stream << "static void qtscript_" + << qtScriptEnumName << "_fromScriptValue(" + << "const QScriptValue &value, " << qualifiedEnumName << " &out)" << endl + << "{" << endl + << " out = qvariant_cast<" << qualifiedEnumName << ">(value.toVariant());" << endl + << "}" << endl << endl; + + // write constructor + stream << "static QScriptValue qtscript_construct_" + << qtScriptEnumName + << "(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " int arg = context->argument(0).toInt32();" << endl; + if (enom->hasQEnumsDeclaration() && (meta_class->qualifiedCppName() != "QTransform")) { + stream << " const QMetaObject *meta = qtscript_" << meta_class->name() << "_metaObject();" << endl; + stream << " int idx = meta->indexOfEnumerator(\"" << enom->name() << "\");" << endl; + stream << " Q_ASSERT(idx != -1);" << endl; + stream << " QMetaEnum menum = meta->enumerator(idx);" << endl; + stream << " if (menum.valueToKey(arg) != 0)" << endl; + stream << " return qScriptValueFromValue(engine, static_cast<" + << qualifiedEnumName << ">(arg));" << endl; + } else { + if (contiguous) { + stream << " if ((arg >= " << meta_class->qualifiedCppName() + << "::" << values.at(uniqueIndexes.first())->name() << ")" + << " && (arg <= " << meta_class->qualifiedCppName() + << "::" << values.at(uniqueIndexes.last())->name() << "))" << endl; + stream << " return qScriptValueFromValue(engine, static_cast<" + << qualifiedEnumName << ">(arg));" << endl; + } else { + stream << " for (int i = 0; i < " << uniqueIndexes.size() << "; ++i) {" << endl + << " if (qtscript_" << qtScriptEnumName << "_values[i] == arg)" << endl; + stream << " return qScriptValueFromValue(engine, static_cast<" + << qualifiedEnumName << ">(arg));" << endl; + stream << " }" << endl; + } + } + stream << " return context->throwError(QString::fromLatin1(\"" + << enom->name() << "(): invalid enum value (%0)\").arg(arg));" << endl; + stream << "}" << endl; + stream << endl; + + // write prototype.valueOf() + stream << "static QScriptValue qtscript_" << qtScriptEnumName + << "_valueOf(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " " << qualifiedEnumName << " value = " + << "qscriptvalue_cast<" << qualifiedEnumName + << ">(context->thisObject());" << endl; + stream << " return QScriptValue(engine, static_cast<int>(value));" << endl; + stream << "}" << endl; + stream << endl; + + // write prototype.toString() + stream << "static QScriptValue qtscript_" << qtScriptEnumName + << "_toString(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " " << qualifiedEnumName << " value = " + << "qscriptvalue_cast<" << qualifiedEnumName + << ">(context->thisObject());" << endl; + stream << " return QScriptValue(engine, qtscript_" << qtScriptEnumName << "_toStringHelper(value));" << endl; + stream << "}" << endl; + stream << endl; + + // write class creation function + stream << "static QScriptValue qtscript_create_" + << qtScriptEnumName + << "_class(QScriptEngine *engine, QScriptValue &clazz)" << endl; + stream << "{" << endl; + + stream << " QScriptValue ctor = qtscript_create_enum_class_helper(" << endl + << " engine, qtscript_construct_" << qtScriptEnumName << "," << endl + << " qtscript_" << qtScriptEnumName << "_valueOf, qtscript_" + << qtScriptEnumName << "_toString);" << endl; + + stream << " qScriptRegisterMetaType<" << qualifiedEnumName << ">(engine, " + << "qtscript_" << qtScriptEnumName << "_toScriptValue," << endl + << " qtscript_" << qtScriptEnumName << "_fromScriptValue," + << " ctor.property(QString::fromLatin1(\"prototype\")));" << endl; + + // enum values are properties of the constructor + stream << " for (int i = 0; i < " << uniqueIndexes.size() << "; ++i) {" << endl + << " clazz.setProperty(QString::fromLatin1(qtscript_" + << qtScriptEnumName << "_keys[i])," << endl + << " engine->newVariant(qVariantFromValue(qtscript_" + << qtScriptEnumName << "_values[i]))," << endl + << " QScriptValue::ReadOnly | QScriptValue::Undeletable);" << endl + << " }" << endl; + + stream << " return ctor;" << endl; + stream << "}" << endl; + stream << endl; + + // write flags class too, if any + FlagsTypeEntry *flags = enom->typeEntry()->flags(); + if (!flags) + return; + + QString qualifiedFlagsName = meta_class->qualifiedCppName() + "::" + flags->targetLangName(); + QString qtScriptFlagsName = meta_class->name() + "_" + flags->targetLangName(); + + stream << "//" << endl; + stream << "// " << qualifiedFlagsName << endl; + stream << "//" << endl << endl; + + // write QScriptValue <--> C++ conversion functions + stream << "static QScriptValue qtscript_" + << qtScriptFlagsName << "_toScriptValue(" + << "QScriptEngine *engine, const " << qualifiedFlagsName << " &value)" << endl + << "{" << endl + << " return engine->newVariant(qVariantFromValue(value));" << endl + << "}" << endl << endl; + stream << "static void qtscript_" + << qtScriptFlagsName << "_fromScriptValue(" + << "const QScriptValue &value, " << qualifiedFlagsName << " &out)" << endl + << "{" << endl + << " QVariant var = value.toVariant();" << endl + << " if (var.userType() == qMetaTypeId<" << qualifiedFlagsName << ">())" << endl + << " out = qvariant_cast<" << qualifiedFlagsName << ">(var);" << endl + << " else if (var.userType() == qMetaTypeId<" << qualifiedEnumName << ">())" << endl + << " out = qvariant_cast<" << qualifiedEnumName << ">(var);" << endl + << " else" << endl + << " out = 0;" << endl + << "}" << endl << endl; + + // write constructor + stream << "static QScriptValue qtscript_construct_" + << qtScriptFlagsName + << "(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " " << qualifiedFlagsName << " result = 0;" << endl; + stream << " if ((context->argumentCount() == 1) && context->argument(0).isNumber()) {" << endl; + stream << " result = static_cast<" << qualifiedFlagsName << ">(context->argument(0).toInt32());" << endl; + stream << " } else {" << endl; + stream << " for (int i = 0; i < context->argumentCount(); ++i) {" << endl; + stream << " QVariant v = context->argument(i).toVariant();" << endl; + stream << " if (v.userType() != qMetaTypeId<" << qualifiedEnumName << ">()) {" << endl; + stream << " return context->throwError(QScriptContext::TypeError," << endl + << " QString::fromLatin1(\"" << flags->targetLangName() + << "(): argument %0 is not of type " << enom->name() << "\").arg(i));" << endl; + stream << " }" << endl; + stream << " result |= qvariant_cast<" << qualifiedEnumName + << ">(v);" << endl; + stream << " }" << endl; + stream << " }" << endl; + stream << " return engine->newVariant(qVariantFromValue(result));" << endl; + stream << "}" << endl; + stream << endl; + + // write prototype.valueOf() + stream << "static QScriptValue qtscript_" << qtScriptFlagsName + << "_valueOf(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " " << qualifiedFlagsName << " value = " + << "qscriptvalue_cast<" << qualifiedFlagsName + << ">(context->thisObject());" << endl; + stream << " return QScriptValue(engine, static_cast<int>(value));" << endl; + stream << "}" << endl; + stream << endl; + + // write prototype.toString() + stream << "static QScriptValue qtscript_" << qtScriptFlagsName + << "_toString(QScriptContext *context, QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " " << qualifiedFlagsName << " value = " + << "qscriptvalue_cast<" << qualifiedFlagsName + << ">(context->thisObject());" << endl; + stream << " QString result;" << endl; + stream << " for (int i = 0; i < " << uniqueIndexes.size() << "; ++i) {" << endl + << " if ((value & qtscript_" << qtScriptEnumName << "_values[i])" + << " == qtscript_" << qtScriptEnumName << "_values[i]) {" << endl + << " if (!result.isEmpty())" << endl + << " result.append(QString::fromLatin1(\",\"));" << endl + << " result.append(QString::fromLatin1(qtscript_" << qtScriptEnumName << "_keys[i]));" << endl + << " }" << endl + << " }" << endl + << " return QScriptValue(engine, result);" << endl + << "}" << endl + << endl; + + // write prototype.equals() + stream << "static QScriptValue qtscript_" << qtScriptFlagsName + << "_equals(QScriptContext *context, QScriptEngine *engine)" << endl + << "{" << endl + << " QVariant thisObj = context->thisObject().toVariant();" << endl + << " QVariant otherObj = context->argument(0).toVariant();" << endl + + << " return QScriptValue(engine, ((thisObj.userType() == otherObj.userType()) &&" << endl + << " (thisObj.value<" << qualifiedFlagsName << ">() == otherObj.value<" << qualifiedFlagsName << ">())));" << endl + << "}" << endl << endl; + + // write class creation function + stream << "static QScriptValue qtscript_create_" << qtScriptFlagsName << "_class(QScriptEngine *engine)" << endl; + stream << "{" << endl; + stream << " QScriptValue ctor = qtscript_create_flags_class_helper(" << endl + << " engine, qtscript_construct_" << qtScriptFlagsName + << ", qtscript_" << qtScriptFlagsName << "_valueOf," << endl + << " qtscript_" << qtScriptFlagsName << "_toString, qtscript_" + << qtScriptFlagsName << "_equals);" << endl; + + stream << " qScriptRegisterMetaType<" << qualifiedFlagsName << ">(engine, " + << "qtscript_" << qtScriptFlagsName << "_toScriptValue," << endl + << " qtscript_" << qtScriptFlagsName << "_fromScriptValue," + << " ctor.property(QString::fromLatin1(\"prototype\")));" << endl; + + stream << " return ctor;" << endl; + stream << "}" << endl; + stream << endl; +} + +/*! + Declares the given \a typeName if it hasn't been declared already, + and adds it to the set of registered type names. +*/ +void maybeDeclareMetaType(QTextStream &stream, const QString &typeName, + QSet<QString> ®isteredTypeNames) +{ + QString name = typeName; + if (name.endsWith(QLatin1Char('&'))) + name.chop(1); + if (registeredTypeNames.contains(name) || (QMetaType::type(typeName.toLatin1()) != 0)) + return; + if (name.contains(QLatin1Char(','))) { + // need to expand the Q_DECLARE_METATYPE macro manually, + // otherwise the compiler will choke + stream << "template <> \\" << endl + << "struct QMetaTypeId< " << name << " > \\" << endl + << "{ \\" << endl + << " enum { Defined = 1 }; \\" << endl + << " static int qt_metatype_id() \\" << endl + << " { \\" << endl + << " static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \\" << endl + << " if (!metatype_id) \\" << endl + << " metatype_id = qRegisterMetaType< " << name << " >(\"" << name << "\"); \\" << endl + << " return metatype_id; \\" << endl + << " } \\" << endl + << "};" << endl; + } else { + stream << "Q_DECLARE_METATYPE(" << name << ")" << endl; + } + registeredTypeNames << name; +} + +/*! + Declares the given \a type recursively (i.e. subtypes of a composite + type are also declared). +*/ +static void declareTypeRecursive(QTextStream &stream, const AbstractMetaType *type, + QSet<QString> ®isteredTypeNames) +{ + if (!type) + return; + QList<AbstractMetaType *> subTypes = type->instantiations(); + for (int i = 0; i < subTypes.size(); ++i) + declareTypeRecursive(stream, subTypes.at(i), registeredTypeNames); + QString typeName = normalizedType(type); + if (typeName == QLatin1String("QStringList<QString>")) + return; // ### wtf... + maybeDeclareMetaType(stream, typeName, registeredTypeNames); +} + +/*! + Declares the types associated with the given \a functions. +*/ +void declareFunctionMetaTypes(QTextStream &stream, const AbstractMetaFunctionList &functions, + QSet<QString> ®isteredTypeNames) +{ + for (int i = 0; i < functions.size(); ++i) { + AbstractMetaFunction *fun = functions.at(i); + if (isSpecialStreamingOperator(fun)) { + maybeDeclareMetaType(stream, fun->arguments().at(0)->type()->name() + "*", + registeredTypeNames); + continue; + } + AbstractMetaArgumentList arguments = fun->arguments(); + for (int j = 0; j < arguments.size(); ++j) { + if (fun->argumentRemoved(j+1)) + continue; + QString repl = fun->typeReplaced(j+1); + if (!repl.isEmpty()) { + maybeDeclareMetaType(stream, repl, registeredTypeNames); + } else { + const AbstractMetaArgument *arg = arguments.at(j); + declareTypeRecursive(stream, arg->type(), registeredTypeNames); + } + } + QString retRepl = fun->typeReplaced(0); + if (!retRepl.isEmpty()) + maybeDeclareMetaType(stream, retRepl, registeredTypeNames); + else + declareTypeRecursive(stream, fun->type(), registeredTypeNames); + } +} + +/*! + Returns true if we don't care about the given enum \a enom, + false otherwise. +*/ +static bool shouldIgnoreEnum(const AbstractMetaEnum *enom) +{ + return !enom->wasPublic() || (enom->name() == "enum_1"); +} + +/*! + Declares the types associated with the enums of the given \a + meta_class. +*/ +void declareEnumMetaTypes(QTextStream &stream, const AbstractMetaClass *meta_class, + QSet<QString> ®isteredTypeNames) +{ + AbstractMetaEnumList enums = meta_class->enums(); + for (int i = 0; i < enums.size(); ++i) { + const AbstractMetaEnum *enom = enums.at(i); + if (shouldIgnoreEnum(enom)) + continue; + maybeDeclareMetaType(stream, QString::fromLatin1("%0::%1") + .arg(meta_class->qualifiedCppName()).arg(enom->name()), + registeredTypeNames); + FlagsTypeEntry *flags = enom->typeEntry()->flags(); + if (flags) { + maybeDeclareMetaType(stream, QString::fromLatin1("QFlags<%0::%1>") + .arg(meta_class->qualifiedCppName()).arg(enom->name()), + registeredTypeNames); + } + } +} + +/*! + Returns the maximum function length among \a functions. +*/ +static int maxFunctionLength(const AbstractMetaFunctionList &functions) +{ + int result = 0; + for (int i = 0; i < functions.size(); ++i) + result = qMax(result, functions.at(i)->arguments().size()); + return result; +} + +/*! + Writes a prototype/static function. +*/ +static void writeFunctionForwarding(QTextStream &stream, const AbstractMetaClass *meta_class, + const AbstractMetaFunctionList &functions) +{ +#if 0 + stream << "/** signatures:" << endl; + foreach (const AbstractMetaFunction *fun, functions) { + stream << " * " << fun->signature() << endl; + } + stream << " */" << endl; +#endif + QMap<int, AbstractMetaFunctionList> argcToFunctions; + argcToFunctions = createArgcToFunctionsMap(functions); + QSet<QString> signatures; + int argcMin = argcToFunctions.keys().first(); + int argcMax = argcToFunctions.keys().last(); + for (int i = argcMin; i <= argcMax; ++i) { + AbstractMetaFunctionList funcs = argcToFunctions.value(i); + if (funcs.isEmpty()) + continue; + stream << " if (context->argumentCount() == " << i << ") {" << endl; + if (funcs.size() == 1) { + AbstractMetaFunction *fun = funcs.at(0); + const int indent = 8; + // special case for Q{Data,Text}Stream streaming operators + if (isSpecialStreamingOperator(fun)) + writeStreamingOperatorCall(stream, fun, meta_class, indent); + else + writeFunctionCallAndReturn(stream, fun, i, meta_class, indent); + signatures.insert(fun->targetLangSignature()); + } else { + // handle overloads + QStringList sigs; + for (int j = 0; j < funcs.size(); ++j) { + AbstractMetaFunction *fun = funcs.at(j); + sigs.append(fun->signature()); + stream << " "; + if (j > 0) + stream << "} else "; + stream << "if ("; + AbstractMetaArgumentList arguments = fun->arguments(); + const int indent = 12; + writeArgumentTypeTests(stream, fun, arguments, i, indent); + stream << ") {" << endl; + writeFunctionCallAndReturn(stream, fun, i, meta_class, indent); + signatures.insert(fun->targetLangSignature()); + } + stream << " }" << endl; + } + stream << " }" << endl; + } +} + +static void writePrototypeCall(QTextStream &s, const AbstractMetaClass *meta_class, + const QMap<QString, AbstractMetaFunctionList> &nameToFunctions, + int prototypeFunctionsOffset) +{ + s << "static QScriptValue qtscript_" << meta_class->name() + << "_prototype_call(QScriptContext *context, QScriptEngine *)" << endl + << "{" << endl; + + s << "#if QT_VERSION > 0x040400" << endl; + + s << " Q_ASSERT(context->callee().isFunction());" << endl + << " uint _id = context->callee().data().toUInt32();" << endl; + + s << "#else" << endl + << " uint _id;" << endl + << " if (context->callee().isFunction())" << endl + << " _id = context->callee().data().toUInt32();" << endl + << " else" << endl + << " _id = 0xBABE0000 + " << nameToFunctions.size() << ";" << endl; + + s << "#endif" << endl; + + s << " Q_ASSERT((_id & 0xFFFF0000) == 0xBABE0000);" << endl + << " _id &= 0x0000FFFF;" << endl; + + // cast the thisObject to C++ type + s << " "; +#ifndef GENERATOR_NO_PROTECTED_FUNCTIONS + if (meta_class->hasProtectedFunctions()) + s << "qtscript_"; +#endif + s << meta_class->qualifiedCppName() << "* _q_self = "; +#ifndef GENERATOR_NO_PROTECTED_FUNCTIONS + if (meta_class->hasProtectedFunctions()) + s << "reinterpret_cast<qtscript_" << meta_class->name() << "*>("; +#endif + s << "qscriptvalue_cast<" << meta_class->qualifiedCppName() + << "*>(context->thisObject())"; +#ifndef GENERATOR_NO_PROTECTED_FUNCTIONS + if (meta_class->hasProtectedFunctions()) + s << ")"; +#endif + s << ";" << endl + << " if (!_q_self) {" << endl + << " return context->throwError(QScriptContext::TypeError," << endl + << " QString::fromLatin1(\"" << meta_class->name() + << ".%0(): this object is not a " << meta_class->name() << "\")" << endl + << " .arg(qtscript_" << meta_class->name() << "_function_names[_id+1]));" << endl + << " }" << endl << endl; + + s << " switch (_id) {" << endl; + + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + int index = 0; + for (it = nameToFunctions.constBegin(); it != nameToFunctions.constEnd(); ++it) { + s << " case " << index << ":" << endl; + writeFunctionForwarding(s, meta_class, it.value()); + s << " break;" << endl << endl; + ++index; + } + + if (!meta_class->hasDefaultToStringFunction()) { + s << " case " << index << ": {" << endl; + s << " QString result"; + FunctionModelItem fun = meta_class->hasToStringCapability(); + if (fun) { + int indirections = fun->arguments().at(1)->type().indirections(); + QString deref = QLatin1String(indirections == 0 ? "*" : ""); + s << ";" << endl + << " QDebug d(&result);" << endl + << " d << " << deref << "_q_self;" << endl; + } else { + // ### FIXME: can cause compile error +// s << "=QString(\"" << meta_class->name() << "(0x%1)\").arg((int)_q_self, 0, 16);" << endl; + s << " = QString::fromLatin1(\"" << meta_class->name() << "\");" << endl; + } + s << " return QScriptValue(context->engine(), result);" << endl + << " }" << endl << endl; + } + + s << " default:" << endl + << " Q_ASSERT(false);" << endl + << " }" << endl; + + s << " return qtscript_" << meta_class->name() << "_throw_ambiguity_error_helper(context," << endl + << " qtscript_" << meta_class->name() + << "_function_names[_id+" << prototypeFunctionsOffset << "]," << endl + << " qtscript_" << meta_class->name() + << "_function_signatures[_id+" << prototypeFunctionsOffset << "]);" << endl; + + s << "}" << endl << endl; +} + +static void writeStaticCall(QTextStream &s, const AbstractMetaClass *meta_class, + const AbstractMetaFunctionList &constructors, + const QMap<QString, AbstractMetaFunctionList> &nameToFunctions) +{ + s << "static QScriptValue qtscript_" << meta_class->name() + << "_static_call(QScriptContext *context, QScriptEngine *)" << endl + << "{" << endl; + + s << " uint _id = context->callee().data().toUInt32();" << endl + << " Q_ASSERT((_id & 0xFFFF0000) == 0xBABE0000);" << endl + << " _id &= 0x0000FFFF;" << endl; + + s << " switch (_id) {" << endl; + + s << " case 0:" << endl; + writeConstructorForwarding(s, constructors, meta_class); + s << " break;" << endl << endl; + + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + int index = 1; + for (it = nameToFunctions.constBegin(); it != nameToFunctions.constEnd(); ++it) { + s << " case " << index << ":" << endl; + writeFunctionForwarding(s, meta_class, it.value()); + s << " break;" << endl << endl; + ++index; + } + + s << " default:" << endl + << " Q_ASSERT(false);" << endl + << " }" << endl; + + s << " return qtscript_" << meta_class->name() << "_throw_ambiguity_error_helper(context," << endl + << " qtscript_" << meta_class->name() << "_function_names[_id]," << endl + << " qtscript_" << meta_class->name() << "_function_signatures[_id]);" << endl; + + s << "}" << endl << endl; +} + +/*! + Writes the include defined by \a inc to \a stream. +*/ +void writeInclude(QTextStream &stream, const Include &inc) +{ + if (inc.name.isEmpty()) + return; + if (inc.type == Include::TargetLangImport) + return; + stream << "#include "; + if (inc.type == Include::IncludePath) + stream << "<"; + else + stream << "\""; + stream << inc.name; + if (inc.type == Include::IncludePath) + stream << ">"; + else + stream << "\""; + stream << endl; +} + +static void writeHelperFunctions(QTextStream &stream, const AbstractMetaClass *meta_class) +{ + stream << "static QScriptValue qtscript_" << meta_class->name() << "_throw_ambiguity_error_helper(" << endl + << " QScriptContext *context, const char *functionName, const char *signatures)" << endl + << "{" << endl + << " QStringList lines = QString::fromLatin1(signatures).split(QLatin1Char('\\n'));" << endl + << " QStringList fullSignatures;" << endl + << " for (int i = 0; i < lines.size(); ++i)" << endl + << " fullSignatures.append(QString::fromLatin1(\"%0(%1)\").arg(functionName).arg(lines.at(i)));" << endl + << " return context->throwError(QString::fromLatin1(\"QFile::%0(): could not find a function match; candidates are:\\n%1\")" << endl + << " .arg(functionName).arg(fullSignatures.join(QLatin1String(\"\\n\"))));" << endl + << "}" << endl << endl; +} + +void writeQtScriptQtBindingsLicense(QTextStream &stream) +{ + stream + << "/****************************************************************************" << endl + << "**" << endl + << "** Copyright (C) 2008 Trolltech ASA. All rights reserved." << endl + << "**" << endl + << "** This file is part of the Qt Script Qt Bindings project on Trolltech Labs." << endl + << "**" << endl + << "** This file may be used under the terms of the GNU General Public" << endl + << "** License version 2.0 as published by the Free Software Foundation" << endl + << "** and appearing in the file LICENSE.GPL included in the packaging of" << endl + << "** this file. Please review the following information to ensure GNU" << endl + << "** General Public Licensing requirements will be met:" << endl + << "** http://www.trolltech.com/products/qt/opensource.html" << endl + << "**" << endl + << "** If you are unsure which license is appropriate for your use, please" << endl + << "** review the following information:" << endl + << "** http://www.trolltech.com/products/qt/licensing.html or contact the" << endl + << "** sales department at sales@trolltech.com." << endl + << "**" << endl + << "** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE" << endl + << "** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << endl + << "**" << endl + << "****************************************************************************/" << endl + << endl; +} + +/*! + Finds the functions in \a meta_class that we actually want to + generate bindings for. +*/ +void findPrototypeAndStaticFunctions( + const AbstractMetaClass *meta_class, + QMap<QString, AbstractMetaFunctionList> &nameToPrototypeFunctions, + QMap<QString, AbstractMetaFunctionList> &nameToStaticFunctions) +{ + AbstractMetaFunctionList functions = meta_class->functionsInTargetLang(); + for (int i = 0; i < functions.size(); ++i) { + AbstractMetaFunction* func = functions.at(i); + if (!func->isNormal()) + continue; +#ifdef GENERATOR_NO_PROTECTED_FUNCTIONS + if (func->wasProtected()) + continue; +#endif + if (func->declaringClass() != meta_class) + continue; // function inherited through prototype + if (func->isPropertyReader() || func->isPropertyWriter()) + continue; // no point in including property accessors + if (func->isSlot() || func->isSignal() || func->isInvokable()) + continue; // no point in including signals and slots + QMap<QString, AbstractMetaFunctionList> &map = + func->isStatic() ? nameToStaticFunctions : nameToPrototypeFunctions; + map[func->modifiedName()].append(func); + } +} + +static void writeFunctionSignaturesString(QTextStream &s, const AbstractMetaFunctionList &functions) +{ + s << "\""; + for (int i = 0; i < functions.size(); ++i) { + if (i > 0) + s << "\\n"; + QString sig = functions.at(i)->targetLangSignature(); + sig = sig.mid(sig.indexOf('(') + 1); + sig.chop(1); + s << sig; + } + s << "\""; +} + +/*! + Writes the whole native binding for the class \a meta_class. +*/ +void ClassGenerator::write(QTextStream &stream, const AbstractMetaClass *meta_class) +{ + if (meta_class->name() == "Global") // ### hmmmmmm + return; + + if (FileOut::license) + writeQtScriptQtBindingsLicense(stream); + + // write common includes + stream << "#include <QtScript/QScriptEngine>" << endl; + stream << "#include <QtScript/QScriptContext>" << endl; + stream << "#include <QtScript/QScriptValue>" << endl; + stream << "#include <QtCore/QStringList>" << endl; + stream << "#include <QtCore/QDebug>" << endl; + stream << "#include <qmetaobject.h>" << endl; + stream << endl; + + // write class-specific includes + { + Include inc = meta_class->typeEntry()->include(); + writeInclude(stream, inc); + } + { + IncludeList includes = meta_class->typeEntry()->extraIncludes(); + qSort(includes.begin(), includes.end()); + + foreach (const Include &i, includes) { + writeInclude(stream, i); + } + } + stream << endl; + + if (meta_class->generateShellClass()) { + stream << "#include \"qtscriptshell_" << meta_class->name() << ".h\"" << endl; + stream << endl; + } + + AbstractMetaEnumList enums = meta_class->enums(); + { + // kill the enums we don't care about + AbstractMetaEnumList::iterator it; + for (it = enums.begin(); it != enums.end(); ) { + if (shouldIgnoreEnum(*it)) + it = enums.erase(it); + else + ++it; + } + } + + // find constructors + AbstractMetaFunctionList ctors = findConstructors(meta_class); + bool hasDefaultCtor = findDefaultConstructor(ctors) != 0; + + // find interesting functions + QMap<QString, AbstractMetaFunctionList> nameToPrototypeFunctions; + QMap<QString, AbstractMetaFunctionList> nameToStaticFunctions; + findPrototypeAndStaticFunctions(meta_class, nameToPrototypeFunctions, nameToStaticFunctions); + + int staticFunctionsOffset = 1; + int prototypeFunctionsOffset = staticFunctionsOffset + nameToStaticFunctions.size(); + + // write table of function names + stream << "static const char * const qtscript_" + << meta_class->name() << "_function_names[] = {" << endl; + stream << " \"" << meta_class->name() << "\"" << endl; + { + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + stream << " // static" << endl; + for (it = nameToStaticFunctions.constBegin(); it != nameToStaticFunctions.constEnd(); ++it) { + stream << " , "; + stream << "\"" << it.key() << "\"" << endl; + } + stream << " // prototype" << endl; + for (it = nameToPrototypeFunctions.constBegin(); it != nameToPrototypeFunctions.constEnd(); ++it) { + QString functionName = it.key(); + QString scriptName = functionName; + if (functionName == QLatin1String("operator_equal")) + scriptName = QLatin1String("equals"); + stream << " , "; + stream << "\"" << scriptName << "\"" << endl; + } + if (!meta_class->hasDefaultToStringFunction()) + stream << " , \"toString\"" << endl; + } + stream << "};" << endl << endl; + + // write table of function signatures + stream << "static const char * const qtscript_" + << meta_class->name() << "_function_signatures[] = {" << endl; + stream << " "; + writeFunctionSignaturesString(stream, ctors); + stream << endl; + { + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + stream << " // static" << endl; + for (it = nameToStaticFunctions.constBegin(); it != nameToStaticFunctions.constEnd(); ++it) { + stream << " , "; + writeFunctionSignaturesString(stream, it.value()); + stream << endl; + } + stream << " // prototype" << endl; + for (it = nameToPrototypeFunctions.constBegin(); it != nameToPrototypeFunctions.constEnd(); ++it) { + stream << " , "; + writeFunctionSignaturesString(stream, it.value()); + stream << endl; + } + if (!meta_class->hasDefaultToStringFunction()) + stream << "\"\"" << endl; + } + stream << "};" << endl << endl; + +#ifndef GENERATOR_NO_PROTECTED_FUNCTIONS + if (meta_class->hasProtectedFunctions()) { + // write a friendly class + stream << "class qtscript_" << meta_class->name() + << " : public " << meta_class->qualifiedCppName() << endl; + stream << "{" << endl; + for (int x = 0; x < 2; ++x) { + QMap<QString, AbstractMetaFunctionList> &map = + x ? nameToStaticFunctions : nameToPrototypeFunctions; + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + for (it = map.constBegin(); it != map.constEnd(); ++it) { + AbstractMetaFunctionList functions = it.value(); + for (int i = 0; i < functions.size(); ++i) { + if (functions.at(i)->isProtected()) { + stream << " friend QScriptValue qtscript_" << meta_class->name() + << "_" << it.key(); + if (functions.at(i)->isStatic()) + stream << "_static"; + stream << "(QScriptContext *, QScriptEngine *);" << endl; + break; + } + } + } + } + stream << "};" << endl; + stream << endl; + } +#endif + + writeHelperFunctions(stream, meta_class); + + // write metaobject getter if we need it + if (hasQEnums(enums) && (meta_class->qualifiedCppName() != "QTransform")) { + if (meta_class->qualifiedCppName() == "Qt") { + stream << "struct qtscript_Qt_metaObject_helper : private QObject" << endl + << "{" << endl + << " static const QMetaObject *get()" << endl + << " { return &static_cast<qtscript_Qt_metaObject_helper*>(0)->staticQtMetaObject; }" << endl + << "};" << endl << endl; + } + stream << "static const QMetaObject *qtscript_" << meta_class->name() << "_metaObject()" << endl + << "{" << endl + << " return "; + if (meta_class->qualifiedCppName() == "Qt") + stream << "qtscript_Qt_metaObject_helper::get()"; + else + stream << "&" << meta_class->qualifiedCppName() << "::staticMetaObject"; + stream << ";" << endl + << "}" << endl << endl; + } + + // write metatype declarations + { + QSet<QString> registeredTypeNames = m_qmetatype_declared_typenames; + + if (!meta_class->isNamespace()) { + if (meta_class->typeEntry()->isValue() && hasDefaultCtor) + maybeDeclareMetaType(stream, meta_class->qualifiedCppName(), registeredTypeNames); + else + registeredTypeNames << meta_class->qualifiedCppName(); + maybeDeclareMetaType(stream, meta_class->qualifiedCppName() + "*", registeredTypeNames); + } + if (meta_class->generateShellClass()) { + if (meta_class->typeEntry()->isValue()) { + maybeDeclareMetaType(stream, "QtScriptShell_" + meta_class->name(), + registeredTypeNames); + } + maybeDeclareMetaType(stream, "QtScriptShell_" + meta_class->name() + "*", + registeredTypeNames); + } + + declareEnumMetaTypes(stream, meta_class, registeredTypeNames); + + for (int x = 0; x < 2; ++x) { + QMap<QString, AbstractMetaFunctionList> &map = + x ? nameToStaticFunctions : nameToPrototypeFunctions; + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + for (it = map.constBegin(); it != map.constEnd(); ++it) { + declareFunctionMetaTypes(stream, it.value(), registeredTypeNames); + } + } + + declareFunctionMetaTypes(stream, ctors, registeredTypeNames); + + if (meta_class->baseClass() != 0) { + maybeDeclareMetaType(stream, meta_class->baseClass()->qualifiedCppName() + + QLatin1String("*"), registeredTypeNames); + } + foreach (AbstractMetaClass *iface, meta_class->interfaces()) { + AbstractMetaClass *impl = iface->primaryInterfaceImplementor(); + maybeDeclareMetaType(stream, impl->qualifiedCppName() + QLatin1String("*"), + registeredTypeNames); + } + + // ### hackety hack + if (meta_class->name().endsWith("Gradient")) + maybeDeclareMetaType(stream, "QGradient", registeredTypeNames); + + stream << endl; + } + + writeInjectedCode(stream, meta_class, CodeSnip::Beginning); + + // write enum classes + if (!enums.isEmpty()) { + writeCreateEnumClassHelper(stream); + if (hasFlags(enums)) + writeCreateFlagsClassHelper(stream); + + for (int i = 0; i < enums.size(); ++i) { + const AbstractMetaEnum *enom = enums.at(i); + writeEnumClass(stream, meta_class, enom); + } + } + + stream << "//" << endl; + stream << "// " << meta_class->name() << endl; + stream << "//" << endl << endl; + + if (!meta_class->isNamespace()) { + if (!nameToPrototypeFunctions.isEmpty() || !meta_class->hasDefaultToStringFunction()) + writePrototypeCall(stream, meta_class, nameToPrototypeFunctions, prototypeFunctionsOffset); + } + + writeStaticCall(stream, meta_class, ctors, nameToStaticFunctions); + + if (isQObjectBased(meta_class)) { + // write C++ <--> script conversion functions + stream << "static QScriptValue qtscript_" << meta_class->name() << "_toScriptValue(QScriptEngine *engine, " + << meta_class->qualifiedCppName() << "* const &in)" << endl + << "{" << endl + << " return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject);" << endl + << "}" << endl << endl; + stream << "static void qtscript_" << meta_class->name() << "_fromScriptValue(const QScriptValue &value, " + << meta_class->qualifiedCppName() << "* &out)" << endl + << "{" << endl + << " out = qobject_cast<" << meta_class->qualifiedCppName() << "*>(value.toQObject());" << endl + << "}" << endl << endl; + } + + // + // write exported function that creates the QtScript class + // + stream << "QScriptValue qtscript_create_" << meta_class->name() + << "_class(QScriptEngine *engine)" << endl; + stream << "{" << endl; + + // write lengths array + stream << " static const int function_lengths[] = {" << endl; + stream << " " << maxFunctionLength(ctors) << endl; + { + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + stream << " // static" << endl; + for (it = nameToStaticFunctions.constBegin(); it != nameToStaticFunctions.constEnd(); ++it) { + stream << " , " << maxFunctionLength(it.value()) << endl; + } + stream << " // prototype" << endl; + for (it = nameToPrototypeFunctions.constBegin(); it != nameToPrototypeFunctions.constEnd(); ++it) { + stream << " , " << maxFunctionLength(it.value()) << endl; + } + if (!meta_class->hasDefaultToStringFunction()) + stream << " , 0" << endl; + } + stream << " };" << endl; + + // setup prototype + if (!meta_class->isNamespace()) { + stream << " engine->setDefaultPrototype(qMetaTypeId<" + << meta_class->qualifiedCppName() << "*>(), QScriptValue());" << endl; + stream << " QScriptValue proto = engine->newVariant(qVariantFromValue((" + << meta_class->qualifiedCppName() << "*)0));" << endl; + bool havePrototypePrototype = false; + if (meta_class->baseClass() != 0) { + stream << " proto.setPrototype(engine->defaultPrototype(qMetaTypeId<" + << meta_class->baseClass()->qualifiedCppName() << "*>()));" << endl; + havePrototypePrototype = true; + } + foreach (AbstractMetaClass *iface, meta_class->interfaces()) { + AbstractMetaClass *impl = iface->primaryInterfaceImplementor(); + if (impl == meta_class) + continue; + if (!havePrototypePrototype) { + stream << " proto.setPrototype(engine->defaultPrototype(qMetaTypeId<" + << impl->qualifiedCppName() << "*>()));" << endl; + havePrototypePrototype = true; + } else { + // alternative would be to copy the properties from the secondary + // prototype to the primary prototype. + stream << " proto.setProperty(QString::fromLatin1(\"__" + << impl->name() << "__\")," << endl + << " engine->defaultPrototype(qMetaTypeId<" + << impl->qualifiedCppName() << "*>())," << endl + << " QScriptValue::SkipInEnumeration);" << endl; + } + } + if (!nameToPrototypeFunctions.isEmpty()) { + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + int count = nameToPrototypeFunctions.size(); + if (!meta_class->hasDefaultToStringFunction()) + ++count; + stream << " for (int i = 0; i < " << count << "; ++i) {" << endl + << " QScriptValue fun = engine->newFunction(qtscript_" + << meta_class->name() << "_prototype_call, function_lengths[i+" + << prototypeFunctionsOffset << "]);" << endl + << " fun.setData(QScriptValue(engine, uint(0xBABE0000 + i)));" << endl + << " proto.setProperty(QString::fromLatin1(qtscript_" + << meta_class->name() << "_function_names[i+" << prototypeFunctionsOffset << "])," << endl + << " fun, QScriptValue::SkipInEnumeration);" << endl + << " }" << endl; + } + writeInjectedCode(stream, meta_class, CodeSnip::PrototypeInitialization); + stream << endl; + + // register the prototype +// stream << " qDebug() << \"registering " << meta_class->name() << " prototype\";" << endl; + if (meta_class->typeEntry()->isValue() && hasDefaultCtor) { + stream << " engine->setDefaultPrototype(qMetaTypeId<" + << meta_class->qualifiedCppName() << ">(), proto);" << endl; + } + if (isQObjectBased(meta_class)) { + stream << " qScriptRegisterMetaType<" << meta_class->qualifiedCppName() << "*>(engine, qtscript_" + << meta_class->name() << "_toScriptValue, " << endl << " qtscript_" + << meta_class->name() << "_fromScriptValue, proto);" << endl; + } else { + stream << " engine->setDefaultPrototype(qMetaTypeId<" + << meta_class->qualifiedCppName() << "*>(), proto);" << endl; + } + stream << endl; + } else { + stream << " QScriptValue proto = QScriptValue();" << endl; + } + + // setup constructor + stream << " QScriptValue ctor = engine->newFunction(qtscript_" << meta_class->name() + << "_static_call, proto, function_lengths[0]);" << endl; + stream << " ctor.setData(QScriptValue(engine, uint(0xBABE0000 + 0)));" << endl; + if (!nameToStaticFunctions.isEmpty()) { + // static functions + QMap<QString, AbstractMetaFunctionList>::const_iterator it; + stream << " for (int i = 0; i < " << nameToStaticFunctions.size() << "; ++i) {" << endl + << " QScriptValue fun = engine->newFunction(qtscript_" << meta_class->name() + << "_static_call," << endl + << " function_lengths[i+" << staticFunctionsOffset << "]);" << endl + << " fun.setData(QScriptValue(engine, uint(0xBABE0000 + i+1)));" << endl + << " ctor.setProperty(QString::fromLatin1(qtscript_" + << meta_class->name() << "_function_names[i+" << staticFunctionsOffset << "])," << endl + << " fun, QScriptValue::SkipInEnumeration);" << endl + << " }" << endl; + } + stream << endl; + // enums and flags classes + { + for (int i = 0; i < enums.size(); ++i) { + const AbstractMetaEnum *enom = enums.at(i); + stream << " ctor.setProperty(QString::fromLatin1(\"" + << enom->name() << "\")," << endl + << " qtscript_create_" << meta_class->name() + << "_" << enom->name() << "_class(engine, ctor));" << endl; + FlagsTypeEntry *flags = enom->typeEntry()->flags(); + if (flags) { + stream << " ctor.setProperty(QString::fromLatin1(\"" + << flags->targetLangName() << "\")," << endl + << " qtscript_create_" << meta_class->name() + << "_" << flags->targetLangName() << "_class(engine));" << endl; + } + } + } + + writeInjectedCode(stream, meta_class, CodeSnip::ConstructorInitialization); + + stream << " return ctor;" << endl; + stream << "}" << endl; + + writeInjectedCode(stream, meta_class, CodeSnip::End); + + QString pro_file_name = meta_class->package().replace(".", "_") + "/" + meta_class->package().replace(".", "_") + ".pri"; + priGenerator->addSource(pro_file_name, fileNameForClass(meta_class)); + setupGenerator->addClass(meta_class); +} |