##// END OF EJS Templates
added experimental compacting to avoid generation of hundreds of files that all include the same stuff...
added experimental compacting to avoid generation of hundreds of files that all include the same stuff git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@128 ea8d5007-eb21-0410-b261-ccb3ea6e24a9

File last commit:

r92:f97ec94c87a9
r92:f97ec94c87a9
Show More
classgenerator.cpp
1842 lines | 75.2 KiB | text/x-c | CppLexer
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 /****************************************************************************
**
florianlink
updated generator to LGPLed version from gitorious...
r87 ** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 **
florianlink
updated generator to LGPLed version from gitorious...
r87 ** This file is part of the Qt Script Generator project on Qt Labs.
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 **
florianlink
updated generator to LGPLed version from gitorious...
r87 ** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 **
florianlink
updated generator to LGPLed version from gitorious...
r87 ** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 **
florianlink
updated generator to LGPLed version from gitorious...
r87 ** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 **
****************************************************************************/
#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
{
return false; // DISABLED!
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("float"))
return QLatin1String("isNumber");
else if (typeName == QLatin1String("int"))
return QLatin1String("isNumber");
else if (typeName == QLatin1String("uint"))
return QLatin1String("isNumber");
else if (typeName == QLatin1String("short"))
return QLatin1String("isNumber");
else if (typeName == QLatin1String("unsigned short"))
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 if (typeName == "QVariant") {
stream << "true";
} 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.
*/
bool ClassGenerator::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,
florianlink
- removed phonon temporarily...
r11 const AbstractMetaClass * /*meta_class*/, int indent)
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 {
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)
{
florianlink
updated generator to LGPLed version from gitorious...
r87 QString qualifiedCppNameColons;
if (meta_class->name() != "Global")
qualifiedCppNameColons = meta_class->qualifiedCppName() + "::";
QString qualifiedEnumName = qualifiedCppNameColons + enom->name();
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 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 << ", ";
florianlink
updated generator to LGPLed version from gitorious...
r87 stream << qualifiedCppNameColons << values.at(uniqueIndexes.at(i))->name() << endl;
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 }
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) {
florianlink
updated generator to LGPLed version from gitorious...
r87 stream << " if ((value >= " << qualifiedCppNameColons
<< values.at(uniqueIndexes.first())->name() << ")"
<< " && (value <= " << qualifiedCppNameColons
<< values.at(uniqueIndexes.last())->name() << "))" << endl
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 << " return qtscript_" << qtScriptEnumName
<< "_keys[static_cast<int>(value)-static_cast<int>("
florianlink
updated generator to LGPLed version from gitorious...
r87 << qualifiedCppNameColons
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 << values.at(uniqueIndexes.first())->name() << ")];" << 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) {
florianlink
updated generator to LGPLed version from gitorious...
r87 stream << " if ((arg >= " << qualifiedCppNameColons
<< values.at(uniqueIndexes.first())->name() << ")"
<< " && (arg <= " << qualifiedCppNameColons
<< values.at(uniqueIndexes.last())->name() << "))" << endl;
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 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;
florianlink
updated generator to LGPLed version from gitorious...
r87 QString qualifiedFlagsName = qualifiedCppNameColons + flags->targetLangName();
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 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> &registeredTypeNames)
{
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> &registeredTypeNames)
{
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> &registeredTypeNames)
{
for (int i = 0; i < functions.size(); ++i) {
AbstractMetaFunction *fun = functions.at(i);
if (ClassGenerator::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> &registeredTypeNames)
{
AbstractMetaEnumList enums = meta_class->enums();
for (int i = 0; i < enums.size(); ++i) {
const AbstractMetaEnum *enom = enums.at(i);
if (shouldIgnoreEnum(enom))
continue;
florianlink
updated generator to LGPLed version from gitorious...
r87 if (meta_class->name() == "Global")
maybeDeclareMetaType(stream, enom->name(), registeredTypeNames);
else
maybeDeclareMetaType(stream, QString::fromLatin1("%0::%1")
.arg(meta_class->qualifiedCppName()).arg(enom->name()),
registeredTypeNames);
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 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 (ClassGenerator::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+" << prototypeFunctionsOffset <<"]));" << 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 ClassGenerator::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(\"" << meta_class->name()
<< "::%0(): could not find a function match; candidates are:\\n%1\")" << endl
<< " .arg(functionName).arg(fullSignatures.join(QLatin1String(\"\\n\"))));" << 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)
{
// 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;
}
}
florianlink
updated generator to LGPLed version from gitorious...
r87 if (meta_class->isNamespace() || meta_class->name() == "Global") {
QMap<QString, Include> includes;
foreach (AbstractMetaEnum *enom, enums) {
Include include = enom->typeEntry()->include();
includes.insert(include.toString(), include);
}
foreach (const Include &i, includes) {
writeInclude(stream, i);
}
stream << endl;
}
if (meta_class->name() == "Global") {
stream << "class Global {};" << endl;
stream << endl;
}
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 // 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;
florianlink
updated generator to LGPLed version from gitorious...
r87 // write table of function lengths
stream << "static const int qtscript_" << meta_class->name() << "_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 << endl;
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 #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;
// 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_"
florianlink
updated generator to LGPLed version from gitorious...
r87 << meta_class->name() << "_prototype_call, qtscript_"
<< meta_class->name() << "_function_lengths[i+"
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 << 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()
florianlink
updated generator to LGPLed version from gitorious...
r87 << "_static_call, proto, qtscript_" << meta_class->name() << "_function_lengths[0]);" << endl;
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 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
florianlink
updated generator to LGPLed version from gitorious...
r87 << " qtscript_" << meta_class->name() << "_function_lengths[i+" << staticFunctionsOffset << "]);" << endl
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 << " 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);
florianlink
updated generator to LGPLed version from gitorious...
r87 QString packName = meta_class->package().replace(".", "_");
priGenerator->addSource(packName, fileNameForClass(meta_class));
florianlink
syncing with my current work, updating to 1.2, see changelog...
r10 setupGenerator->addClass(meta_class);
}