qtdeclarative/src/qml/compiler/qqmlcodegenerator_p.h

362 lines
11 KiB
C
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QQMLCODEGENERATOR_P_H
#define QQMLCODEGENERATOR_P_H
#include <private/qqmljsast_p.h>
#include <private/qqmlpool_p.h>
#include <private/qqmlscript_p.h>
#include <private/qqmljsengine_p.h>
#include <private/qv4compiler_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
#include <QTextStream>
#include <QCoreApplication>
QT_BEGIN_NAMESPACE
namespace QtQml {
using namespace QQmlJS;
struct DebugStream
{
DebugStream(QTextStream &stream)
: stream(stream)
, indent(0)
{}
template <typename T>
QTextStream &operator<<(const T &value)
{
return stream << QByteArray(indent * 4, ' ') << value;
}
QTextStream &noindent() { return stream; }
QTextStream &stream;
int indent;
};
template <typename T>
struct PoolList
{
PoolList()
: first(0)
, last(0)
, count(0)
{}
T *first;
T *last;
int count;
void append(T *item) {
item->next = 0;
if (last)
last->next = item;
else
first = item;
last = item;
++count;
}
};
struct QmlObject;
struct SignalParameter : public QV4::CompiledData::Parameter
{
SignalParameter *next;
};
struct Signal
{
int nameIndex;
QV4::CompiledData::Location location;
PoolList<SignalParameter> *parameters;
QStringList parameterStringList(const QStringList &stringPool) const;
Signal *next;
};
struct QmlProperty : public QV4::CompiledData::Property
{
QmlProperty *next;
};
struct Binding : public QV4::CompiledData::Binding
{
Binding *next;
};
struct Function
{
int index;
Function *next;
};
struct QmlObject
{
int inheritedTypeNameIndex;
int idIndex;
int indexOfDefaultProperty;
QV4::CompiledData::Location location;
QV4::CompiledData::Location locationOfIdProperty;
PoolList<QmlProperty> *properties;
PoolList<Signal> *qmlSignals;
PoolList<Binding> *bindings;
PoolList<Function> *functions;
void dump(DebugStream &out);
};
struct ParsedQML
{
ParsedQML()
: jsGenerator(&jsModule, sizeof(QV4::CompiledData::QmlUnit))
{}
QString code;
QQmlJS::Engine jsParserEngine;
V4IR::Module jsModule;
QList<QV4::CompiledData::Import*> imports;
AST::UiProgram *program;
int indexOfRootObject;
QList<QmlObject*> objects;
QList<AST::Node*> functions; // FunctionDeclaration, Statement or Expression
QV4::Compiler::JSUnitGenerator jsGenerator;
QV4::CompiledData::TypeReferenceMap typeReferences;
QString stringAt(int index) const { return jsGenerator.strings.value(index); }
};
// Doesn't really generate code per-se, but more the data structure
struct Q_QML_EXPORT QQmlCodeGenerator : public AST::Visitor
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
public:
QQmlCodeGenerator();
bool generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output);
static bool isSignalPropertyName(const QString &name);
using AST::Visitor::visit;
using AST::Visitor::endVisit;
virtual bool visit(AST::UiArrayMemberList *ast);
virtual bool visit(AST::UiImport *ast);
Add Singleton support for QML This introduces Singleton support for QML (Composite Singleton). For now, the Singleton support is only availabe for QML types in modules or (remote and local) directories with qmldir file. However, in the future this support may be expanded to arbitrary QML file imports without by leaving out the qmldir requirement. You define a QML type as a Singleton with the following two steps: 1. By adding a pragma Singleton to a type's QML file: pragma Singleton The pragma and import statements can be mixed and their order does not matter. Singleton is the only supported pragma for now. Others will generate errors. 2. By specifying a qmldir file for the directory of your imported type and prepending the type with "singleton" keyword as follows: singleton TestTypeSingleton TestTypeSingleton.qml Alternatively you may specify a qmldir file for a module and specify your type as a singleton as follows: singleton TestTypeSingleton 1.0 TestTypeSingleton.qml Composite Singletons may be included in a module and may be used with a local namespace qualifier when imported with: "import xxx as NameSpace" A singleton instance is created at first use and stored into the QmlEngine (one instance per engine) and eventually released by the engine's destructor. CompositeSingletonType has a dual nature and will return true to both isComposite() and isSingleton() calls. In most cases its enough to check for just isComposite() or isSingleton(). However, there is a isCompositeSingleton() available as well. I used "qlalr --no-debug --no-lines --qt qqmljs.g" to generate the qqmljsparser and qqmljsgrammar files from qqmljs.g. Unit tests are included. Change-Id: I91b303612c5e132143b325b9a8f982e9355bc90e Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com>
2013-08-22 19:08:37 +00:00
virtual bool visit(AST::UiPragma *ast);
virtual bool visit(AST::UiHeaderItemList *ast);
virtual bool visit(AST::UiObjectInitializer *ast);
virtual bool visit(AST::UiObjectMemberList *ast);
virtual bool visit(AST::UiParameterList *ast);
virtual bool visit(AST::UiProgram *);
virtual bool visit(AST::UiQualifiedId *ast);
virtual bool visit(AST::UiArrayBinding *ast);
virtual bool visit(AST::UiObjectBinding *ast);
virtual bool visit(AST::UiObjectDefinition *ast);
virtual bool visit(AST::UiPublicMember *ast);
virtual bool visit(AST::UiScriptBinding *ast);
virtual bool visit(AST::UiSourceElement *ast);
void accept(AST::Node *node);
// returns index in _objects
int defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer);
int defineQMLObject(AST::UiObjectDefinition *node)
{ return defineQMLObject(node->qualifiedTypeNameId, node->initializer); }
static QString asString(AST::UiQualifiedId *node);
QStringRef asStringRef(AST::Node *node);
static void extractVersion(QStringRef string, int *maj, int *min);
QStringRef textRefAt(const AST::SourceLocation &loc) const
{ return QStringRef(&sourceCode, loc.offset, loc.length); }
QStringRef textRefAt(const AST::SourceLocation &first,
const AST::SourceLocation &last) const;
static QQmlScript::LocationSpan location(AST::UiQualifiedId *id)
{
return location(id->identifierToken, id->identifierToken);
}
void setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement);
void appendBinding(AST::UiQualifiedId *name, AST::Statement *value);
void appendBinding(AST::UiQualifiedId *name, int objectIndex);
void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value);
void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false);
bool setId(AST::Statement *value);
// resolves qualified name (font.pixelSize for example) and returns the last name along
// with the object any right-hand-side of a binding should apply to.
bool resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object);
bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem = false);
void recordError(const AST::SourceLocation &location, const QString &description);
void collectTypeReferences();
static QQmlScript::LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
int registerString(const QString &str) const { return jsGenerator->registerString(str); }
template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
QString stringAt(int index) const { return jsGenerator->strings.at(index); }
static bool isStatementNodeScript(AST::Statement *statement);
QList<QQmlError> errors;
QList<QV4::CompiledData::Import*> _imports;
QList<QmlObject*> _objects;
QList<AST::Node*> _functions;
QV4::CompiledData::TypeReferenceMap _typeReferences;
QmlObject *_object;
QSet<QString> _propertyNames;
QSet<QString> _signalNames;
QQmlJS::MemoryPool *pool;
QString sourceCode;
QUrl url;
QV4::Compiler::JSUnitGenerator *jsGenerator;
int emptyStringIndex;
bool sanityCheckFunctionNames();
};
struct Q_QML_EXPORT QmlUnitGenerator
{
QmlUnitGenerator()
: jsUnitGenerator(0)
{
}
QV4::CompiledData::QmlUnit *generate(ParsedQML &output);
private:
int getStringId(const QString &str) const;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
};
struct PropertyResolver
{
PropertyResolver(QQmlPropertyCache *cache)
: cache(cache)
{}
QQmlPropertyData *property(int index)
{
return cache->property(index);
}
QQmlPropertyData *property(const QString &name, bool *notInRevision = 0);
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
QQmlPropertyData *signal(const QString &name, bool *notInRevision);
QQmlPropertyCache *cache;
};
// "Converts" signal expressions to full-fleged function declarations with
// parameters taken from the signal declarations
// It also updates the QV4::CompiledData::Binding objects to set the property name
// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag.
struct SignalHandlerConverter
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
public:
SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, ParsedQML *parsedQML,
QQmlCompiledData *unit);
bool convertSignalHandlerExpressionsToFunctionDeclarations();
QList<QQmlError> errors;
private:
bool convertSignalHandlerExpressionsToFunctionDeclarations(QmlObject *obj, const QString &typeName, QQmlPropertyCache *propertyCache);
const QString &stringAt(int index) const { return parsedQML->jsGenerator.strings.at(index); }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
QQmlEnginePrivate *enginePrivate;
ParsedQML *parsedQML;
QQmlCompiledData *unit;
};
struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen
{
JSCodeGen()
: QQmlJS::Codegen(/*strict mode*/false)
{}
void generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output);
private:
struct QmlScanner : public ScanFunctions
{
QmlScanner(JSCodeGen *cg, const QString &sourceCode)
: ScanFunctions(cg, sourceCode, /*default program mode*/GlobalCode)
, codeGen(cg)
{}
void begin(AST::Node *rootNode, CompilationMode compilationMode);
void end();
JSCodeGen *codeGen;
};
V4IR::Module jsModule;
};
} // namespace QtQml
QT_END_NAMESPACE
#endif // QQMLCODEGENERATOR_P_H