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>
This commit is contained in:
Antti Piira 2013-08-22 12:08:37 -07:00 committed by The Qt Project
parent b365471f0a
commit 200a869441
73 changed files with 2749 additions and 1318 deletions

View File

@ -128,7 +128,7 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co
sourceCode = code;
accept(program->imports);
accept(program->headers);
if (program->members->next) {
QQmlError error;
@ -241,7 +241,7 @@ bool QQmlCodeGenerator::visit(AST::UiArrayBinding *node)
return false;
}
bool QQmlCodeGenerator::visit(AST::UiImportList *list)
bool QQmlCodeGenerator::visit(AST::UiHeaderItemList *list)
{
return AST::Visitor::visit(list);
}
@ -418,6 +418,11 @@ bool QQmlCodeGenerator::visit(AST::UiImport *node)
return false;
}
bool QQmlCodeGenerator::visit(AST::UiPragma *ast)
{
return true;
}
static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
{
if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {

View File

@ -188,7 +188,8 @@ public:
virtual bool visit(AST::UiArrayMemberList *ast);
virtual bool visit(AST::UiImport *ast);
virtual bool visit(AST::UiImportList *ast);
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);

View File

@ -937,7 +937,13 @@ bool Codegen::visit(UiImport *)
return false;
}
bool Codegen::visit(UiImportList *)
bool Codegen::visit(UiHeaderItemList *)
{
assert(!"unreachable");
return false;
}
bool Codegen::visit(UiPragma *)
{
assert(!"unreachable");
return false;
@ -973,6 +979,12 @@ bool Codegen::visit(UiQualifiedId *)
return false;
}
bool Codegen::visit(UiQualifiedPragmaId *)
{
assert(!"unreachable");
return false;
}
bool Codegen::visit(VariableDeclaration *)
{
assert(!"unreachable");

View File

@ -326,12 +326,14 @@ protected:
virtual bool visit(AST::StatementList *ast);
virtual bool visit(AST::UiArrayMemberList *ast);
virtual bool visit(AST::UiImport *ast);
virtual bool visit(AST::UiImportList *ast);
virtual bool visit(AST::UiHeaderItemList *ast);
virtual bool visit(AST::UiPragma *ast);
virtual bool visit(AST::UiObjectInitializer *ast);
virtual bool visit(AST::UiObjectMemberList *ast);
virtual bool visit(AST::UiParameterList *ast);
virtual bool visit(AST::UiProgram *ast);
virtual bool visit(AST::UiQualifiedId *ast);
virtual bool visit(AST::UiQualifiedPragmaId *ast);
virtual bool visit(AST::VariableDeclaration *ast);
virtual bool visit(AST::VariableDeclarationList *ast);

View File

@ -65,6 +65,7 @@
--- context keywords.
%token T_PUBLIC "public"
%token T_IMPORT "import"
%token T_PRAGMA "pragma"
%token T_AS "as"
%token T_ON "on"
%token T_GET "get"
@ -253,7 +254,8 @@ public:
AST::VariableDeclarationList *VariableDeclarationList;
AST::UiProgram *UiProgram;
AST::UiImportList *UiImportList;
AST::UiHeaderItemList *UiHeaderItemList;
AST::UiPragma *UiPragma;
AST::UiImport *UiImport;
AST::UiParameterList *UiParameterList;
AST::UiPublicMember *UiPublicMember;
@ -266,6 +268,7 @@ public:
AST::UiObjectMemberList *UiObjectMemberList;
AST::UiArrayMemberList *UiArrayMemberList;
AST::UiQualifiedId *UiQualifiedId;
AST::UiQualifiedPragmaId *UiQualifiedPragmaId;
};
public:
@ -347,6 +350,7 @@ protected:
{ return location_stack [tos + index - 1]; }
AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
protected:
Engine *driver;
@ -486,6 +490,19 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
return 0;
}
AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode *expr)
{
if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
AST::UiQualifiedPragmaId *q = new (pool) AST::UiQualifiedPragmaId(idExpr->name);
q->identifierToken = idExpr->identifierToken;
return q->finish();
}
return 0;
}
bool Parser::parse(int startToken)
{
Lexer *lexer = driver->lexer();
@ -594,38 +611,62 @@ case $rule_number: {
} break;
./
UiProgram: UiImportListOpt UiRootMember ;
UiProgram: UiHeaderItemListOpt UiRootMember;
/.
case $rule_number: {
sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList,
sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList,
sym(2).UiObjectMemberList->finish());
} break;
./
UiImportListOpt: Empty ;
UiImportListOpt: UiImportList ;
UiHeaderItemListOpt: Empty ;
UiHeaderItemListOpt: UiHeaderItemList ;
/.
case $rule_number: {
sym(1).Node = sym(1).UiImportList->finish();
sym(1).Node = sym(1).UiHeaderItemList->finish();
} break;
./
UiImportList: UiImport ;
UiHeaderItemList: UiPragma ;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport);
sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
} break;
./
UiImportList: UiImportList UiImport ;
UiHeaderItemList: UiImport ;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport);
sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
} break;
./
UiHeaderItemList: UiHeaderItemList UiPragma ;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
} break;
./
UiHeaderItemList: UiHeaderItemList UiImport ;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
} break;
./
PragmaId: MemberExpression ;
ImportId: MemberExpression ;
UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON ;
UiPragma: UiPragmaHead T_SEMICOLON ;
/.
case $rule_number: {
sym(1).UiPragma->semicolonToken = loc(2);
} break;
./
UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ;
UiImport: UiImportHead T_SEMICOLON ;
/.
@ -666,6 +707,28 @@ case $rule_number: {
} break;
./
UiPragmaHead: T_PRAGMA PragmaId ;
/.
case $rule_number: {
AST::UiPragma *node = 0;
if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) {
node = new (pool) AST::UiPragma(qualifiedId);
}
sym(1).Node = node;
if (node) {
node->pragmaToken = loc(1);
} else {
diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
QLatin1String("Expected a qualified name id")));
return false; // ### remove me
}
} break;
./
UiImportHead: T_IMPORT ImportId ;
/.
@ -1261,6 +1324,7 @@ case $rule_number: {
} break;
./
UiQualifiedId: MemberExpression ;
/.
case $rule_number: {

View File

@ -821,7 +821,7 @@ void DebuggerStatement::accept0(Visitor *visitor)
void UiProgram::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(imports, visitor);
accept(headers, visitor);
accept(members, visitor);
}
@ -932,16 +932,34 @@ void UiImport::accept0(Visitor *visitor)
visitor->endVisit(this);
}
void UiImportList::accept0(Visitor *visitor)
void UiQualifiedPragmaId::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(import, visitor);
}
visitor->endVisit(this);
}
void UiPragma::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(pragmaType, visitor);
}
visitor->endVisit(this);
}
void UiHeaderItemList::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(headerItem, visitor);
accept(next, visitor);
}
visitor->endVisit(this);
}
void UiSourceElement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {

View File

@ -207,18 +207,20 @@ public:
Kind_UiArrayBinding,
Kind_UiImport,
Kind_UiImportList,
Kind_UiObjectBinding,
Kind_UiObjectDefinition,
Kind_UiObjectInitializer,
Kind_UiObjectMemberList,
Kind_UiArrayMemberList,
Kind_UiPragma,
Kind_UiProgram,
Kind_UiParameterList,
Kind_UiPublicMember,
Kind_UiQualifiedId,
Kind_UiQualifiedPragmaId,
Kind_UiScriptBinding,
Kind_UiSourceElement
Kind_UiSourceElement,
Kind_UiHeaderItemList
};
inline Node()
@ -2271,44 +2273,6 @@ public:
SourceLocation semicolonToken;
};
class QML_PARSER_EXPORT UiImportList: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiImportList)
UiImportList(UiImport *import)
: import(import),
next(this)
{ kind = K; }
UiImportList(UiImportList *previous, UiImport *import)
: import(import)
{
kind = K;
next = previous->next;
previous->next = this;
}
UiImportList *finish()
{
UiImportList *head = next;
next = 0;
return head;
}
virtual void accept0(Visitor *visitor);
virtual SourceLocation firstSourceLocation() const
{ return import->firstSourceLocation(); }
virtual SourceLocation lastSourceLocation() const
{ return next ? next->lastSourceLocation() : import->lastSourceLocation(); }
// attributes
UiImport *import;
UiImportList *next;
};
class QML_PARSER_EXPORT UiObjectMember: public Node
{
public:
@ -2355,21 +2319,131 @@ public:
UiObjectMember *member;
};
class QML_PARSER_EXPORT UiQualifiedPragmaId: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiQualifiedPragmaId)
UiQualifiedPragmaId(const QStringRef &name)
: next(this), name(name)
{ kind = K; }
UiQualifiedPragmaId(UiQualifiedPragmaId *previous, const QStringRef &name)
: name(name)
{
kind = K;
next = previous->next;
previous->next = this;
}
UiQualifiedPragmaId *finish()
{
UiQualifiedPragmaId *head = next;
next = 0;
return head;
}
virtual void accept0(Visitor *visitor);
virtual SourceLocation firstSourceLocation() const
{ return identifierToken; }
virtual SourceLocation lastSourceLocation() const
{ return next ? next->lastSourceLocation() : identifierToken; }
// attributes
UiQualifiedPragmaId *next;
QStringRef name;
SourceLocation identifierToken;
};
class QML_PARSER_EXPORT UiPragma: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiPragma)
UiPragma(UiQualifiedPragmaId *type)
: pragmaType(type)
{ kind = K; }
virtual void accept0(Visitor *visitor);
virtual SourceLocation firstSourceLocation() const
{ return pragmaToken; }
virtual SourceLocation lastSourceLocation() const
{ return semicolonToken; }
// attributes
UiQualifiedPragmaId *pragmaType;
SourceLocation pragmaToken;
SourceLocation semicolonToken;
};
class QML_PARSER_EXPORT UiHeaderItemList: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiHeaderItemList)
UiHeaderItemList(UiImport *import)
: headerItem(import), next(this)
{ kind = K; }
UiHeaderItemList(UiPragma *pragma)
: headerItem(pragma), next(this)
{ kind = K; }
UiHeaderItemList(UiHeaderItemList *previous, UiImport *import)
: headerItem(import)
{
kind = K;
next = previous->next;
previous->next = this;
}
UiHeaderItemList(UiHeaderItemList *previous, UiPragma *pragma)
: headerItem(pragma)
{
kind = K;
next = previous->next;
previous->next = this;
}
UiHeaderItemList *finish()
{
UiHeaderItemList *head = next;
next = 0;
return head;
}
virtual void accept0(Visitor *visitor);
virtual SourceLocation firstSourceLocation() const
{ return headerItem->firstSourceLocation(); }
virtual SourceLocation lastSourceLocation() const
{ return next ? next->lastSourceLocation() : headerItem->lastSourceLocation(); }
// attributes
Node *headerItem;
UiHeaderItemList *next;
};
class QML_PARSER_EXPORT UiProgram: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiProgram)
UiProgram(UiImportList *imports, UiObjectMemberList *members)
: imports(imports), members(members)
UiProgram(UiHeaderItemList *headers, UiObjectMemberList *members)
: headers(headers), members(members)
{ kind = K; }
virtual void accept0(Visitor *visitor);
virtual SourceLocation firstSourceLocation() const
{
if (imports)
return imports->firstSourceLocation();
if (headers)
return headers->firstSourceLocation();
else if (members)
return members->firstSourceLocation();
return SourceLocation();
@ -2379,13 +2453,13 @@ public:
{
if (members)
return members->lastSourceLocation();
else if (imports)
return imports->lastSourceLocation();
else if (headers)
return headers->lastSourceLocation();
return SourceLocation();
}
// attributes
UiImportList *imports;
UiHeaderItemList *headers;
UiObjectMemberList *members;
};

View File

@ -167,7 +167,7 @@ class NestedExpression;
// ui elements
class UiProgram;
class UiImportList;
class UiPragma;
class UiImport;
class UiPublicMember;
class UiParameterList;
@ -181,6 +181,8 @@ class UiObjectMember;
class UiObjectMemberList;
class UiArrayMemberList;
class UiQualifiedId;
class UiQualifiedPragmaId;
class UiHeaderItemList;
} } // namespace AST

View File

@ -71,7 +71,8 @@ public:
// Ui
virtual bool visit(UiProgram *) { return true; }
virtual bool visit(UiImportList *) { return true; }
virtual bool visit(UiHeaderItemList *) { return true; }
virtual bool visit(UiPragma *) { return true; }
virtual bool visit(UiImport *) { return true; }
virtual bool visit(UiPublicMember *) { return true; }
virtual bool visit(UiSourceElement *) { return true; }
@ -84,10 +85,12 @@ public:
virtual bool visit(UiObjectMemberList *) { return true; }
virtual bool visit(UiArrayMemberList *) { return true; }
virtual bool visit(UiQualifiedId *) { return true; }
virtual bool visit(UiQualifiedPragmaId *) { return true; }
virtual void endVisit(UiProgram *) {}
virtual void endVisit(UiImportList *) {}
virtual void endVisit(UiImport *) {}
virtual void endVisit(UiHeaderItemList *) {}
virtual void endVisit(UiPragma *) {}
virtual void endVisit(UiPublicMember *) {}
virtual void endVisit(UiSourceElement *) {}
virtual void endVisit(UiObjectDefinition *) {}
@ -99,6 +102,7 @@ public:
virtual void endVisit(UiObjectMemberList *) {}
virtual void endVisit(UiArrayMemberList *) {}
virtual void endVisit(UiQualifiedId *) {}
virtual void endVisit(UiQualifiedPragmaId *) {}
// QQmlJS
virtual bool visit(ThisExpression *) { return true; }

File diff suppressed because it is too large Load Diff

View File

@ -63,12 +63,12 @@ class QQmlJSGrammar
public:
enum VariousConstants {
EOF_SYMBOL = 0,
REDUCE_HERE = 104,
SHIFT_THERE = 103,
REDUCE_HERE = 105,
SHIFT_THERE = 104,
T_AND = 1,
T_AND_AND = 2,
T_AND_EQ = 3,
T_AS = 92,
T_AS = 93,
T_AUTOMATIC_SEMICOLON = 62,
T_BREAK = 4,
T_CASE = 5,
@ -90,19 +90,19 @@ public:
T_EQ = 17,
T_EQ_EQ = 18,
T_EQ_EQ_EQ = 19,
T_ERROR = 96,
T_ERROR = 97,
T_FALSE = 83,
T_FEED_JS_EXPRESSION = 100,
T_FEED_JS_PROGRAM = 102,
T_FEED_JS_SOURCE_ELEMENT = 101,
T_FEED_JS_STATEMENT = 99,
T_FEED_UI_OBJECT_MEMBER = 98,
T_FEED_UI_PROGRAM = 97,
T_FEED_JS_EXPRESSION = 101,
T_FEED_JS_PROGRAM = 103,
T_FEED_JS_SOURCE_ELEMENT = 102,
T_FEED_JS_STATEMENT = 100,
T_FEED_UI_OBJECT_MEMBER = 99,
T_FEED_UI_PROGRAM = 98,
T_FINALLY = 20,
T_FOR = 21,
T_FUNCTION = 22,
T_GE = 23,
T_GET = 94,
T_GET = 95,
T_GT = 24,
T_GT_GT = 25,
T_GT_GT_EQ = 26,
@ -130,13 +130,14 @@ public:
T_NOT_EQ_EQ = 46,
T_NULL = 81,
T_NUMERIC_LITERAL = 47,
T_ON = 93,
T_ON = 94,
T_OR = 48,
T_OR_EQ = 49,
T_OR_OR = 50,
T_PLUS = 51,
T_PLUS_EQ = 52,
T_PLUS_PLUS = 53,
T_PRAGMA = 92,
T_PROPERTY = 66,
T_PUBLIC = 90,
T_QUESTION = 54,
@ -149,7 +150,7 @@ public:
T_RETURN = 59,
T_RPAREN = 60,
T_SEMICOLON = 61,
T_SET = 95,
T_SET = 96,
T_SIGNAL = 67,
T_STAR = 63,
T_STAR_EQ = 64,
@ -168,15 +169,15 @@ public:
T_XOR = 79,
T_XOR_EQ = 80,
ACCEPT_STATE = 655,
RULE_COUNT = 351,
STATE_COUNT = 656,
TERMINAL_COUNT = 105,
NON_TERMINAL_COUNT = 108,
ACCEPT_STATE = 663,
RULE_COUNT = 357,
STATE_COUNT = 664,
TERMINAL_COUNT = 106,
NON_TERMINAL_COUNT = 111,
GOTO_INDEX_OFFSET = 656,
GOTO_INFO_OFFSET = 2970,
GOTO_CHECK_OFFSET = 2970
GOTO_INDEX_OFFSET = 664,
GOTO_INFO_OFFSET = 3104,
GOTO_CHECK_OFFSET = 3104
};
static const char *const spell [];

View File

@ -436,6 +436,17 @@ static inline int classify6(const QChar *s, bool qmlMode) {
}
}
}
else if (s[1].unicode() == 'r') {
if (s[2].unicode() == 'a') {
if (s[3].unicode() == 'g') {
if (s[4].unicode() == 'm') {
if (s[5].unicode() == 'a') {
return qmlMode ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER;
}
}
}
}
}
}
else if (s[0].unicode() == 'r') {
if (s[1].unicode() == 'e') {

File diff suppressed because it is too large Load Diff

View File

@ -112,7 +112,8 @@ public:
AST::VariableDeclarationList *VariableDeclarationList;
AST::UiProgram *UiProgram;
AST::UiImportList *UiImportList;
AST::UiHeaderItemList *UiHeaderItemList;
AST::UiPragma *UiPragma;
AST::UiImport *UiImport;
AST::UiParameterList *UiParameterList;
AST::UiPublicMember *UiPublicMember;
@ -125,6 +126,7 @@ public:
AST::UiObjectMemberList *UiObjectMemberList;
AST::UiArrayMemberList *UiArrayMemberList;
AST::UiQualifiedId *UiQualifiedId;
AST::UiQualifiedPragmaId *UiQualifiedPragmaId;
};
public:
@ -206,6 +208,7 @@ protected:
{ return location_stack [tos + index - 1]; }
AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
protected:
Engine *driver;
@ -245,9 +248,9 @@ protected:
#define J_SCRIPT_REGEXPLITERAL_RULE1 81
#define J_SCRIPT_REGEXPLITERAL_RULE1 87
#define J_SCRIPT_REGEXPLITERAL_RULE2 82
#define J_SCRIPT_REGEXPLITERAL_RULE2 88
QT_QML_END_NAMESPACE

View File

@ -821,6 +821,10 @@ bool QQmlCompiler::compile(QQmlEngine *engine,
QQmlScript::TypeReference *parserRef = referencedTypes.at(ii);
if (tref.typeData) { //QML-based type
if (tref.type->isCompositeSingleton()) {
QString err = tr( "Composite Singleton Type %1 is not creatable.").arg(tref.type->qmlTypeName());
COMPILE_EXCEPTION(parserRef->firstUse, err);
}
ref.component = tref.typeData->compiledData();
ref.component->addref();
} else if (tref.type) {//C++-based type
@ -886,6 +890,12 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree)
output->importCache->add(ns);
}
// Add any Composite Singletons that were used to the import cache
for (int i = 0; i < unit->compositeSingletons().count(); ++i) {
output->importCache->add(unit->compositeSingletons().at(i).type->qmlTypeName(),
unit->compositeSingletons().at(i).type->sourceUrl(), unit->compositeSingletons().at(i).prefix);
}
int scriptIndex = 0;
foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
QString qualifier = script.qualifier;
@ -2546,7 +2556,7 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
if (!type && typeName != QLatin1String("Qt"))
return true;
if (type && type->isComposite()) //No enums on composite types
if (type && type->isComposite()) //No enums on composite (or composite singleton) types
return true;
int value = 0;
@ -2984,6 +2994,8 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod
if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, 0, 0, 0))
COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString()));
// We dont mind even if the composite type ends up being composite singleton, here
// we just acquire the metaTypeId.
if (qmltype->isComposite()) {
QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
Q_ASSERT(tdata);

View File

@ -116,7 +116,7 @@ bool QQmlDirParser::parse(const QString &source)
if (ch->isNull())
break;
QString sections[3];
QString sections[4];
int sectionCount = 0;
do {
@ -126,7 +126,7 @@ bool QQmlDirParser::parse(const QString &source)
}
const QChar *start = ch;
scanWord(ch);
if (sectionCount < 3) {
if (sectionCount < 4) {
sections[sectionCount++] = source.mid(start-source.constData(), ch-start);
} else {
reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
@ -167,7 +167,7 @@ bool QQmlDirParser::parse(const QString &source)
_typeNamespace = sections[1];
} else if (sections[0] == QLatin1String("plugin")) {
if (sectionCount < 2) {
if (sectionCount < 2 || sectionCount > 3) {
reportError(lineNumber, 0,
QString::fromUtf8("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1));
@ -187,6 +187,43 @@ bool QQmlDirParser::parse(const QString &source)
Component entry(sections[1], sections[2], -1, -1);
entry.internal = true;
_components.insertMulti(entry.typeName, entry);
} else if (sections[0] == QLatin1String("singleton")) {
if (sectionCount < 3 || sectionCount > 4) {
reportError(lineNumber, 0,
QString::fromUtf8("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1));
continue;
} else if (sectionCount == 3) {
// handle qmldir directory listing case where singleton is defined in the following pattern:
// singleton TestSingletonType TestSingletonType.qml
Component entry(sections[1], sections[2], -1, -1);
entry.singleton = true;
_components.insertMulti(entry.typeName, entry);
} else {
// handle qmldir module listing case where singleton is defined in the following pattern:
// singleton TestSingletonType 2.0 TestSingletonType20.qml
const QString &version = sections[2];
const int dotIndex = version.indexOf(QLatin1Char('.'));
if (dotIndex == -1) {
reportError(lineNumber, 0, QLatin1String("expected '.'"));
} else if (version.indexOf(QLatin1Char('.'), dotIndex + 1) != -1) {
reportError(lineNumber, 0, QLatin1String("unexpected '.'"));
} else {
bool validVersionNumber = false;
const int majorVersion = parseInt(QStringRef(&version, 0, dotIndex), &validVersionNumber);
if (validVersionNumber) {
const int minorVersion = parseInt(QStringRef(&version, dotIndex+1, version.length()-dotIndex-1), &validVersionNumber);
if (validVersionNumber) {
const QString &fileName = sections[3];
Component entry(sections[1], fileName, majorVersion, minorVersion);
entry.singleton = true;
_components.insertMulti(entry.typeName, entry);
}
}
}
}
} else if (sections[0] == QLatin1String("typeinfo")) {
if (sectionCount != 2) {
reportError(lineNumber, 0,

View File

@ -93,17 +93,18 @@ public:
struct Component
{
Component()
: majorVersion(0), minorVersion(0), internal(false) {}
: majorVersion(0), minorVersion(0), internal(false), singleton(false) {}
Component(const QString &typeName, const QString &fileName, int majorVersion, int minorVersion)
: typeName(typeName), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion),
internal(false) {}
internal(false), singleton(false) {}
QString typeName;
QString fileName;
int majorVersion;
int minorVersion;
bool internal;
bool singleton;
};
struct Script

View File

@ -129,25 +129,36 @@ bool isPathAbsolute(const QString &path)
}
// If the type does not already exist as a file import, add the type and return the new type
QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, QList<QQmlError> *errors)
QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList<QQmlError> *errors)
{
QUrl url(urlString);
QQmlType *ret = QQmlMetaType::qmlType(url);
if (!ret) { //QQmlType not yet existing for composite type
if (!ret) { //QQmlType not yet existing for composite or composite singleton type
int dot = typeName.indexOf(QLatin1Char('.'));
QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1);
//XXX: The constData of the string ref is pointing somewhere unsafe in qmlregister, so we need to create a temporary copy
QByteArray buf(unqualifiedtype.toString().toUtf8());
QQmlPrivate::RegisterCompositeType reg = {
url,
"", //Empty URI indicates loaded via file imports
-1,
-1,
buf.constData()
};
ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &reg));
if (isCompositeSingleton) {
QQmlPrivate::RegisterCompositeSingletonType reg = {
url,
"", //Empty URI indicates loaded via file imports
-1,
-1,
buf.constData()
};
ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, &reg));
} else {
QQmlPrivate::RegisterCompositeType reg = {
url,
"", //Empty URI indicates loaded via file imports
-1,
-1,
buf.constData()
};
ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &reg));
}
}
if (!ret) {//Usually when a type name is "found" but invalid
//qDebug() << ret << urlString << QQmlMetaType::qmlType(url);
@ -158,10 +169,9 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa
errors->prepend(error);
}
return ret;
}
}
} // namespace
struct RegisteredPlugin {
QString uri;
@ -362,6 +372,61 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const
}
}
// We need to exclude the entry for the current baseUrl. This can happen for example
// when handling qmldir files on the remote dir case and the current type is marked as
// singleton.
bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString baseUrl)
{
if (importUrl.isEmpty())
return false;
if (baseUrl.startsWith(importUrl))
{
QString typeUrl(importUrl);
typeUrl.append(fileName);
if (typeUrl == baseUrl)
return false;
}
return true;
}
void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, QUrl baseUrl)
{
typedef QQmlDirComponents::const_iterator ConstIterator;
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
const QQmlImportNamespace::Import *import = set.imports.at(ii);
const QQmlDirComponents &components = import->qmlDirComponents;
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
QQmlImports::CompositeSingletonReference ref;
ref.typeName = cit->typeName;
ref.prefix = set.prefix;
resultList.append(ref);
}
}
}
}
QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const
{
QList<QQmlImports::CompositeSingletonReference> compositeSingletons;
const QQmlImportNamespace &set = d->unqualifiedset;
findCompositeSingletons(set, compositeSingletons, baseUrl());
for (QQmlImportNamespace *ns = d->qualifiedSets.first(); ns; ns = d->qualifiedSets.next(ns)) {
const QQmlImportNamespace &set = *ns;
findCompositeSingletons(set, compositeSingletons, baseUrl());
}
return compositeSingletons;
}
QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
{
QList<QQmlImports::ScriptReference> scripts;
@ -452,7 +517,9 @@ bool QQmlImports::resolveType(const QHashedStringRef &type,
#define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
<< ')' << "::resolveType: " << type.toString() << " => "
if (type_return && *type_return && (*type_return)->isComposite())
if (type_return && *type_return && (*type_return)->isCompositeSingleton())
RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << (*type_return)->sourceUrl() << " TYPE/URL-SINGLETON";
else if (type_return && *type_return && (*type_return)->isComposite())
RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << (*type_return)->sourceUrl() << " TYPE/URL";
else if (type_return && *type_return)
RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE";
@ -544,6 +611,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
QQmlDirComponents::ConstIterator it = qmlDirComponents.find(type), end = qmlDirComponents.end();
if (it != end) {
QString componentUrl;
bool isCompositeSingleton = false;
QQmlDirComponents::ConstIterator candidate = end;
for ( ; it != end && it.key() == type; ++it) {
const QQmlDirParser::Component &c = *it;
@ -568,13 +636,14 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
// This is our best candidate so far
candidate = it;
isCompositeSingleton = c.singleton;
}
}
}
if (candidate != end) {
if (type_return)
*type_return = getTypeForUrl(componentUrl, type, 0);
*type_return = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0);
return (*type_return != 0);
}
} else if (!isLibrary) {
@ -594,7 +663,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
*typeRecursionDetected = true;
} else {
if (type_return)
*type_return = getTypeForUrl(qmlUrl, type, 0);
*type_return = getTypeForUrl(qmlUrl, type, false, 0);
return (*type_return) != 0;
}
}
@ -637,7 +706,7 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor,
return true;
if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) {
// qualified, and only 1 url
*type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, errors);
*type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors);
return (*type_return != 0);
}
}

View File

@ -122,6 +122,14 @@ public:
QList<ScriptReference> resolvedScripts() const;
struct CompositeSingletonReference
{
QString typeName;
QString prefix;
};
QList<CompositeSingletonReference> resolvedCompositeSingletons() const;
static QString completeQmldirPath(const QString &uri, const QString &base, int vmaj, int vmin,
QQmlImports::ImportVersion version);

View File

@ -63,6 +63,7 @@
#include <qvector.h>
#include <ctype.h>
#include "qqmlcomponent.h"
QT_BEGIN_NAMESPACE
@ -77,6 +78,10 @@ struct QQmlMetaTypeData
Names nameToType;
typedef QHash<QUrl, QQmlType *> Files; //For file imported composite types only
Files urlToType;
Files urlToNonFileImportType; // For non-file imported composite and composite
// singleton types. This way we can locate any
// of them by url, even if it was registered as
// a module via qmlRegisterCompositeType.
typedef QHash<const QMetaObject *, QQmlType *> MetaObjects;
MetaObjects metaObjectToType;
typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
@ -225,6 +230,10 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
setScriptApi(e, scriptCallback(e, e));
} else if (qobjectCallback && !qobjectApi(e)) {
setQObjectApi(e, qobjectCallback(e, e));
} else if (!url.isEmpty() && !qobjectApi(e)) {
QQmlComponent component(e, url, QQmlComponent::PreferSynchronous);
QObject *o = component.create();
setQObjectApi(e, o);
}
v4->popContext();
}
@ -279,6 +288,7 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
extraData.cd->propertyValueInterceptorCast = -1;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
extraData.sd = new QQmlSingletonTypeData;
extraData.sd->singletonInstanceInfo = 0;
break;
@ -300,6 +310,7 @@ QQmlTypePrivate::~QQmlTypePrivate()
delete extraData.cd;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
delete extraData.sd->singletonInstanceInfo;
delete extraData.sd;
break;
@ -351,6 +362,22 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg
= (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0;
}
QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type)
: d(new QQmlTypePrivate(CompositeSingletonType))
{
d->elementName = elementName;
d->module = QString::fromUtf8(type.uri);
d->version_maj = type.versionMajor;
d->version_min = type.versionMinor;
d->index = index;
d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo;
d->extraData.sd->singletonInstanceInfo->url = type.url;
d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
}
QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterType &type)
: d(new QQmlTypePrivate(CppType))
{
@ -650,7 +677,7 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
QByteArray QQmlType::typeName() const
{
if (d->regType == SingletonType)
if (d->regType == SingletonType || d->regType == CompositeSingletonType)
return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
else if (d->baseMetaObject)
return d->baseMetaObject->className();
@ -710,7 +737,7 @@ void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) con
QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const
{
if (d->regType != SingletonType)
if (d->regType != SingletonType && d->regType != CompositeSingletonType)
return 0;
return d->extraData.sd->singletonInstanceInfo;
}
@ -757,7 +784,7 @@ bool QQmlType::isExtendedType() const
bool QQmlType::isSingleton() const
{
return d->regType == SingletonType;
return d->regType == SingletonType || d->regType == CompositeSingletonType;
}
bool QQmlType::isInterface() const
@ -767,7 +794,12 @@ bool QQmlType::isInterface() const
bool QQmlType::isComposite() const
{
return d->regType == CompositeType;
return d->regType == CompositeType || d->regType == CompositeSingletonType;
}
bool QQmlType::isCompositeSingleton() const
{
return d->regType == CompositeSingletonType;
}
int QQmlType::typeId() const
@ -869,9 +901,12 @@ int QQmlType::index() const
QUrl QQmlType::sourceUrl() const
{
if (d->regType != CompositeType)
if (d->regType == CompositeType)
return d->extraData.fd->url;
else if (d->regType == CompositeSingletonType)
return d->extraData.sd->singletonInstanceInfo->url;
else
return QUrl();
return d->extraData.fd->url;
}
int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const
@ -1006,7 +1041,6 @@ QList<QQmlType*> QQmlTypeModule::singletonTypes(int minor) const
return retn;
}
QQmlTypeModuleVersion::QQmlTypeModuleVersion()
: m_module(0), m_minor(0)
{
@ -1070,6 +1104,7 @@ void qmlClearTypeRegistrations() // Declared in qqml.h
data->idToType.clear();
data->nameToType.clear();
data->urlToType.clear();
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->uriToModule.clear();
@ -1123,6 +1158,8 @@ QString registrationTypeString(QQmlType::RegistrationType typeType)
typeStr = QStringLiteral("element");
else if (typeType == QQmlType::SingletonType)
typeStr = QStringLiteral("singleton type");
else if (typeType == QQmlType::CompositeSingletonType)
typeStr = QStringLiteral("composite singleton type");
else
typeStr = QStringLiteral("type");
return typeStr;
@ -1254,6 +1291,31 @@ int registerSingletonType(const QQmlPrivate::RegisterSingletonType &type)
return index;
}
int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type)
{
// Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type.
QWriteLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
QString typeName = QString::fromUtf8(type.typeName);
bool fileImport = false;
if (*(type.uri) == '\0')
fileImport = true;
if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? 0 : type.uri, typeName))
return -1;
int index = data->types.count();
QQmlType *dtype = new QQmlType(index, typeName, type);
data->types.append(dtype);
addTypeToData(dtype, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
files->insertMulti(type.url, dtype);
return index;
}
int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type)
{
// Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type.
@ -1272,8 +1334,8 @@ int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type)
data->types.append(dtype);
addTypeToData(dtype, data);
if (fileImport)
data->urlToType.insertMulti(type.url, dtype);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
files->insertMulti(type.url, dtype);
return index;
}
@ -1295,6 +1357,8 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
return registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
} else if (type == CompositeRegistration) {
return registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
} else if (type == CompositeSingletonRegistration) {
return registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
}
return -1;
}
@ -1708,12 +1772,15 @@ QQmlType *QQmlMetaType::qmlType(int userType)
Returns null if no such type is registered.
*/
QQmlType *QQmlMetaType::qmlType(const QUrl &url)
QQmlType *QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = false */)
{
QReadLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
QQmlType *type = data->urlToType.value(url);
if (!type && includeNonFileImports)
type = data->urlToNonFileImportType.value(url);
if (type && type->sourceUrl() == url)
return type;
else

View File

@ -86,7 +86,7 @@ public:
static QQmlType *qmlType(const QMetaObject *);
static QQmlType *qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor);
static QQmlType *qmlType(int);
static QQmlType *qmlType(const QUrl &url);
static QQmlType *qmlType(const QUrl &url, bool includeNonFileImports = false);
static QQmlType *qmlTypeFromIndex(int);
static QMetaProperty defaultProperty(const QMetaObject *);
@ -169,6 +169,8 @@ public:
bool isSingleton() const;
bool isInterface() const;
bool isComposite() const;
bool isCompositeSingleton() const;
int typeId() const;
int qListTypeId() const;
@ -198,6 +200,7 @@ public:
QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *);
const QMetaObject *instanceMetaObject;
QString typeName;
QUrl url; // used by composite singletons
void setQObjectApi(QQmlEngine *, QObject *);
QObject *qobjectApi(QQmlEngine *) const;
@ -211,6 +214,7 @@ public:
QHash<QQmlEngine *, QObject *> qobjectApis;
};
SingletonInstanceInfo *singletonInstanceInfo() const;
QUrl sourceUrl() const;
int enumValue(const QHashedStringRef &, bool *ok) const;
@ -225,7 +229,8 @@ private:
CppType = 0,
SingletonType = 1,
InterfaceType = 2,
CompositeType = 3
CompositeType = 3,
CompositeSingletonType = 4
};
friend QString registrationTypeString(RegistrationType);
friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &, int);
@ -233,11 +238,13 @@ private:
friend int registerSingletonType(const QQmlPrivate::RegisterSingletonType &);
friend int registerInterface(const QQmlPrivate::RegisterInterface &);
friend int registerCompositeType(const QQmlPrivate::RegisterCompositeType &);
friend int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &);
friend Q_QML_EXPORT void qmlClearTypeRegistrations();
QQmlType(int, const QQmlPrivate::RegisterInterface &);
QQmlType(int, const QString &, const QQmlPrivate::RegisterSingletonType &);
QQmlType(int, const QString &, const QQmlPrivate::RegisterType &);
QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeType &);
QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &);
~QQmlType();
QQmlTypePrivate *d;

View File

@ -260,12 +260,21 @@ namespace QQmlPrivate
const char *typeName;
};
struct RegisterCompositeSingletonType {
QUrl url;
const char *uri;
int versionMajor;
int versionMinor;
const char *typeName;
};
enum RegistrationType {
TypeRegistration = 0,
InterfaceRegistration = 1,
AutoParentRegistration = 2,
SingletonRegistration = 3,
CompositeRegistration = 4
CompositeRegistration = 4,
CompositeSingletonRegistration = 5
};
int Q_QML_EXPORT qmlregister(RegistrationType, void *);

View File

@ -517,6 +517,7 @@ protected:
virtual bool visit(AST::UiProgram *node);
virtual bool visit(AST::UiImport *node);
virtual bool visit(AST::UiPragma *node);
virtual bool visit(AST::UiObjectDefinition *node);
virtual bool visit(AST::UiPublicMember *node);
virtual bool visit(AST::UiObjectBinding *node);
@ -794,10 +795,10 @@ LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation
return rv;
}
// UiProgram: UiImportListOpt UiObjectMemberList ;
// UiProgram: UiHeaderItemListOpt UiObjectMemberList ;
bool ProcessAST::visit(AST::UiProgram *node)
{
accept(node->imports);
accept(node->headers);
accept(node->members->member);
return false;
}
@ -889,6 +890,41 @@ bool ProcessAST::visit(AST::UiImport *node)
return false;
}
bool ProcessAST::visit(AST::UiPragma *node)
{
QQmlScript::Pragma pragma;
// For now the only valid pragma is Singleton, so lets validate the input
if (!node->pragmaType->name.isNull())
{
if (QLatin1String("Singleton") == node->pragmaType->name.toString())
{
pragma.type = QQmlScript::Pragma::Singleton;
} else {
QQmlError error;
error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier"));
error.setLine(node->pragmaToken.startLine);
error.setColumn(node->pragmaToken.startColumn);
_parser->_errors << error;
return false;
}
} else {
QQmlError error;
error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier"));
error.setLine(node->pragmaToken.startLine);
error.setColumn(node->pragmaToken.startColumn);
_parser->_errors << error;
return false;
}
AST::SourceLocation startLoc = node->pragmaToken;
AST::SourceLocation endLoc = node->semicolonToken;
pragma.location = location(startLoc, endLoc);
_parser->_pragmas << pragma;
return false;
}
bool ProcessAST::visit(AST::UiPublicMember *node)
{
static const struct TypeNameToType {
@ -1364,6 +1400,11 @@ QList<QQmlScript::Import> QQmlScript::Parser::imports() const
return _imports;
}
QList<QQmlScript::Pragma> QQmlScript::Parser::pragmas() const
{
return _pragmas;
}
QList<QQmlError> QQmlScript::Parser::errors() const
{
return _errors;
@ -1416,7 +1457,7 @@ QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QStr
token = l.lex();
if (token != QQmlJSGrammar::T_IDENTIFIER ||
if (token != QQmlJSGrammar::T_PRAGMA ||
l.tokenStartLine() != startLine ||
script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
return rv;
@ -1506,7 +1547,6 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
const QString pragma(QLatin1String("pragma"));
const QString js(QLatin1String(".js"));
const QString library(QLatin1String("library"));
@ -1681,10 +1721,7 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
rv.imports << import;
}
} else if (token == QQmlJSGrammar::T_IDENTIFIER &&
script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
} else if (token == QQmlJSGrammar::T_PRAGMA) {
token = l.lex();
CHECK_TOKEN(T_IDENTIFIER);
@ -1713,6 +1750,7 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
void QQmlScript::Parser::clear()
{
_pragmas.clear();
_imports.clear();
_refTypes.clear();
_errors.clear();

View File

@ -120,6 +120,17 @@ public:
QQmlScript::LocationSpan location;
};
class Pragma
{
public:
Pragma() : type(Singleton) {}
enum Type { Singleton };
Type type;
QQmlScript::LocationSpan location;
};
class Object;
class TypeReference : public QQmlPool::Class
{
@ -474,6 +485,7 @@ public:
QQmlScript::Object *tree() const;
QList<Import> imports() const;
QList<Pragma> pragmas() const;
void clear();
@ -505,6 +517,7 @@ public:
QQmlPool _pool;
QQmlScript::Object *root;
QList<Import> _imports;
QList<Pragma> _pragmas;
QList<TypeReference*> _refTypes;
QString _scriptFile;
ParserJsASTData *data;

View File

@ -1172,7 +1172,7 @@ void QQmlDataLoader::shutdownThread()
}
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
: QQmlDataBlob(url, type), m_typeLoader(loader), m_imports(loader)
: QQmlDataBlob(url, type), m_typeLoader(loader), m_imports(loader), m_isSingleton(false)
{
}
@ -1336,6 +1336,51 @@ bool QQmlTypeLoader::Blob::addImport(const QQmlScript::Import &import, QList<QQm
return true;
}
bool QQmlTypeLoader::Blob::addPragma(const QQmlScript::Pragma &pragma, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
if (pragma.type == QQmlScript::Pragma::Singleton) {
QUrl myUrl = url();
QQmlType *ret = QQmlMetaType::qmlType(myUrl, true);
if (!ret) {
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
error.setUrl(myUrl);
error.setLine(pragma.location.start.line);
error.setColumn(pragma.location.start.column);
errors->prepend(error);
return false;
}
if (!ret->isCompositeSingleton()) {
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(ret->qmlTypeName()));
error.setUrl(myUrl);
error.setLine(pragma.location.start.line);
error.setColumn(pragma.location.start.column);
errors->prepend(error);
return false;
}
// This flag is used for error checking when a qmldir file marks a type as
// composite singleton, but there is no pragma Singleton defined in QML.
m_isSingleton = true;
} else {
QQmlError error;
error.setDescription(QLatin1String("Invalid pragma"));
error.setUrl(url());
error.setLine(pragma.location.start.line);
error.setColumn(pragma.location.start.column);
errors->prepend(error);
return false;
}
return true;
}
void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
{
if (blob->type() == QQmlDataBlob::QmldirFile) {
@ -1941,6 +1986,11 @@ const QSet<QString> &QQmlTypeData::namespaces() const
return m_namespaces;
}
const QList<QQmlTypeData::TypeReference> &QQmlTypeData::compositeSingletons() const
{
return m_compositeSingletons;
}
QQmlCompiledData *QQmlTypeData::compiledData() const
{
return m_compiledData;
@ -2015,6 +2065,36 @@ void QQmlTypeData::done()
}
// ---
// Check all composite singleton type dependencies for errors
for (int ii = 0; !isError() && ii < m_compositeSingletons.count(); ++ii) {
const TypeReference &type = m_compositeSingletons.at(ii);
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
QString typeName = type.type->qmlTypeName();
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
error.setUrl(finalUrl());
error.setLine(type.location.line);
error.setColumn(type.location.column);
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
}
}
// If the type is CompositeSingleton but there was no pragma Singleton in the
// QML file, lets report an error.
QQmlType *type = QQmlMetaType::qmlType(url(), true);
if (type && type->isCompositeSingleton() && !m_isSingleton) {
QString typeName = type->qmlTypeName();
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
error.setUrl(finalUrl());
setError(error);
}
// Compile component
if (!isError())
compile();
@ -2140,6 +2220,14 @@ void QQmlTypeData::dataReceived(const Data &data)
return;
}
}
foreach (const QQmlScript::Pragma &pragma, scriptParser.pragmas()) {
if (!addPragma(pragma, &errors)) {
Q_ASSERT(errors.size());
setError(errors);
return;
}
}
}
void QQmlTypeData::allDependenciesDone()
@ -2327,61 +2415,51 @@ void QQmlTypeData::resolveTypes()
m_scripts << ref;
}
// Lets handle resolved composite singleton types
foreach (const QQmlImports::CompositeSingletonReference &csRef, m_imports.resolvedCompositeSingletons()) {
TypeReference ref;
QQmlScript::TypeReference parserRef;
parserRef.name = csRef.typeName;
// we are basing our type on the information from qmldir and therefore
// do not have a proper location.
parserRef.firstUse = NULL;
if (!csRef.prefix.isEmpty()) {
parserRef.name.prepend(csRef.prefix + QLatin1Char('.'));
// Add a reference to the enclosing namespace
m_namespaces.insert(csRef.prefix);
}
int majorVersion = -1;
int minorVersion = -1;
if (!resolveType(&parserRef, majorVersion, minorVersion, ref))
return;
if (ref.type->isCompositeSingleton()) {
ref.typeData = typeLoader()->getType(ref.type->sourceUrl());
addDependency(ref.typeData);
ref.prefix = csRef.prefix;
m_compositeSingletons << ref;
}
}
// --- old compiler:
foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
TypeReference ref;
int majorVersion = -1;
int minorVersion = -1;
QQmlImportNamespace *typeNamespace = 0;
QList<QQmlError> errors;
bool typeFound = m_imports.resolveType(parserRef->name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
typeFound = m_imports.resolveType(parserRef->name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
} else {
return; //loadImplicitImport() hit an error, and called setError already
}
}
if (!typeFound || typeNamespace) {
// Known to not be a type:
// - known to be a namespace (Namespace {})
// - type with unknown namespace (UnknownNamespace.SomeType {})
QQmlError error;
if (typeNamespace) {
error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
} else {
if (errors.size()) {
error = errors.takeFirst();
} else {
// this should not be possible!
// Description should come from error provided by addImport() function.
error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
}
error.setUrl(m_imports.baseUrl());
error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
}
Q_ASSERT(parserRef->firstUse);
error.setLine(parserRef->firstUse->location.start.line);
error.setColumn(parserRef->firstUse->location.start.column);
errors.prepend(error);
setError(errors);
if (!resolveType(parserRef, majorVersion, minorVersion, ref))
return;
}
if (ref.type->isComposite()) {
ref.typeData = typeLoader()->getType(ref.type->sourceUrl());
addDependency(ref.typeData);
}
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
@ -2466,6 +2544,58 @@ void QQmlTypeData::resolveTypes()
}
}
bool QQmlTypeData::resolveType(const QQmlScript::TypeReference *parserRef, int &majorVersion, int &minorVersion, TypeReference &ref)
{
QQmlImportNamespace *typeNamespace = 0;
QList<QQmlError> errors;
bool typeFound = m_imports.resolveType(parserRef->name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
typeFound = m_imports.resolveType(parserRef->name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
} else {
return false; //loadImplicitImport() hit an error, and called setError already
}
}
if (!typeFound || typeNamespace) {
// Known to not be a type:
// - known to be a namespace (Namespace {})
// - type with unknown namespace (UnknownNamespace.SomeType {})
QQmlError error;
if (typeNamespace) {
error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
} else {
if (errors.size()) {
error = errors.takeFirst();
} else {
// this should not be possible!
// Description should come from error provided by addImport() function.
error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
}
error.setUrl(m_imports.baseUrl());
error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
}
if (parserRef->firstUse)
{
error.setLine(parserRef->firstUse->location.start.line);
error.setColumn(parserRef->firstUse->location.start.column);
}
errors.prepend(error);
setError(errors);
return false;
}
return true;
}
void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
{
ScriptReference ref;

View File

@ -279,6 +279,7 @@ public:
protected:
bool addImport(const QQmlScript::Import &import, QList<QQmlError> *errors);
bool addPragma(const QQmlScript::Pragma &pragma, QList<QQmlError> *errors);
bool fetchQmldir(const QUrl &url, const QQmlScript::Import *import, int priority, QList<QQmlError> *errors);
bool updateQmldir(QQmlQmldirData *data, const QQmlScript::Import *import, QList<QQmlError> *errors);
@ -294,6 +295,7 @@ public:
protected:
QQmlTypeLoader *m_typeLoader;
QQmlImports m_imports;
bool m_isSingleton;
QHash<const QQmlScript::Import *, int> m_unresolvedImports;
QList<QQmlQmldirData *> m_qmldirs;
};
@ -401,6 +403,7 @@ public:
int majorVersion;
int minorVersion;
QQmlTypeData *typeData;
QString prefix; // used by CompositeSingleton types
};
struct ScriptReference
@ -425,6 +428,7 @@ public:
const QList<TypeReference> &resolvedTypes() const;
const QList<ScriptReference> &resolvedScripts() const;
const QSet<QString> &namespaces() const;
const QList<TypeReference> &compositeSingletons() const;
QQmlCompiledData *compiledData() const;
@ -447,6 +451,7 @@ protected:
private:
void resolveTypes();
void compile();
bool resolveType(const QQmlScript::TypeReference *parserRef, int &majorVersion, int &minorVersion, TypeReference &ref);
virtual void scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace);
@ -459,6 +464,7 @@ private:
QList<ScriptReference> m_scripts;
QSet<QString> m_namespaces;
QList<TypeReference> m_compositeSingletons;
// --- old compiler
QList<TypeReference> m_types;

View File

@ -53,6 +53,21 @@ QQmlTypeNameCache::~QQmlTypeNameCache()
{
}
void QQmlTypeNameCache::add(const QHashedString &name, const QUrl &url, const QHashedString &nameSpace)
{
if (nameSpace.length() != 0) {
Import *i = m_namedImports.value(nameSpace);
Q_ASSERT(i != 0);
i->compositeSingletons.insert(name, url);
return;
}
if (m_anonymousCompositeSingletons.contains(name))
return;
m_anonymousCompositeSingletons.insert(name, url);
}
void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, const QHashedString &nameSpace)
{
Import import;
@ -78,6 +93,9 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name)
if (!result.isValid())
result = typeSearch(m_anonymousImports, name);
if (!result.isValid())
result = query(m_anonymousCompositeSingletons, name);
return result;
}
@ -88,7 +106,12 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name,
const Import *i = static_cast<const Import *>(importNamespace);
Q_ASSERT(i->scriptIndex == -1);
return typeSearch(i->modules, name);
Result result = result = typeSearch(i->modules, name);
if (!result.isValid())
result = query(i->compositeSingletons, name);
return result;
}
QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name)
@ -98,6 +121,9 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name)
if (!result.isValid())
result = typeSearch(m_anonymousImports, name);
if (!result.isValid())
result = query(m_anonymousCompositeSingletons, name);
return result;
}
@ -114,7 +140,12 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons
return r;
}
return typeSearch(i->modules, name);
Result r = typeSearch(i->modules, name);
if (!r.isValid())
r = query(i->compositeSingletons, name);
return r;
}
QT_END_NAMESPACE

View File

@ -74,6 +74,7 @@ public:
inline bool isEmpty() const;
void add(const QHashedString &name, int sciptIndex = -1, const QHashedString &nameSpace = QHashedString());
void add(const QHashedString &name, const QUrl &url, const QHashedString &nameSpace = QHashedString());
struct Result {
inline Result();
@ -103,6 +104,9 @@ private:
// Or, imported script
int scriptIndex;
// Or, imported compositeSingletons
QStringHash<QUrl> compositeSingletons;
};
template<typename Key>
@ -120,6 +124,19 @@ private:
return Result();
}
template<typename Key>
Result query(const QStringHash<QUrl> &urls, Key key)
{
QUrl *url = urls.value(key);
if (url) {
QQmlType *type = QQmlMetaType::qmlType(*url);
if (type)
return Result(type);
}
return Result();
}
template<typename Key>
Result typeSearch(const QVector<QQmlTypeModuleVersion> &modules, Key key)
{
@ -135,8 +152,7 @@ private:
QStringHash<Import> m_namedImports;
QMap<const Import *, QStringHash<Import> > m_namespacedImports;
QVector<QQmlTypeModuleVersion> m_anonymousImports;
QQmlEngine *engine;
QStringHash<QUrl> m_anonymousCompositeSingletons;
};
QQmlTypeNameCache::Result::Result()
@ -176,7 +192,8 @@ QQmlTypeNameCache::Import::Import()
bool QQmlTypeNameCache::isEmpty() const
{
return m_namedImports.isEmpty() && m_anonymousImports.isEmpty();
return m_namedImports.isEmpty() && m_anonymousImports.isEmpty()
&& m_anonymousCompositeSingletons.isEmpty();
}
QT_END_NAMESPACE

View File

@ -157,8 +157,7 @@ void tst_qqmldirparser::parse_data()
QTest::newRow("four-sections")
<< "four-sections/qmldir"
<< (QStringList() << "qmldir:1:12: unexpected token"
<< "qmldir:1: invalid qmldir directive contains too many tokens")
<< (QStringList() << "qmldir:1: a component declaration requires two or three arguments, but 4 were provided")
<< QStringList()
<< QStringList()
<< QStringList();
@ -200,8 +199,7 @@ void tst_qqmldirparser::parse_data()
QTest::newRow("excessive-plugin")
<< "excessive-plugin/qmldir"
<< (QStringList() << "qmldir:1:15: unexpected token"
<< "qmldir:1: invalid qmldir directive contains too many tokens")
<< (QStringList() << "qmldir:1: plugin directive requires one or two arguments, but 3 were provided")
<< QStringList()
<< QStringList()
<< QStringList();

View File

@ -0,0 +1,18 @@
import QtQuick 2.0
pragma Singleton
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
.pragma library
var value = 333

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
Item {
id: singletonId
property int testProp1: 225
property int testProp2: 125
property int testProp3: 55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,18 @@
import QtQuick 2.0
pragma Singleton
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,18 @@
pragma Singleton
import QtQuick 2.0
Item {
id: singletonId
property int testProp1: 225
property int testProp2: 125
property int testProp3: 55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,5 @@
module org.qtproject.SingletonTest
singleton SingletonType 1.0 SingletonType.qml
singleton SingletonType 2.2 SingletonType22.qml
NonSingletonType 1.0 NonSingletonType.qml
NonSingletonType 2.5 NonSingletonType25.qml

View File

@ -0,0 +1,3 @@
singleton SingletonType SingletonType.qml

View File

@ -0,0 +1,18 @@
import QtQuick 2.0
pragma Singleton
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1 @@
singleton SType2

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,19 @@
import QtQuick 2.0
pragma Singleton
Item {
id: singletonId
property int testProp1: 325
property int testProp2: 225
property int testProp3: 155
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,18 @@
import QtQuick 2.0
pragma Singleton
Item {
id: singletonId
property int testProp1: 525
property int testProp2: 425
property int testProp3: 355
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1 @@
singleton RemoteSingletonType2 RemoteSingletonType2.qml

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
Item {
id: singletonId
property int testProp1: 125
property int testProp2: 25
property int testProp3: -55
width: 25; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
}

View File

@ -0,0 +1,3 @@
singleton ErrorSingletonType ErrorSingletonType.qml

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
import "singleton"
Item {
property int value1: SingletonType.testProp1;
property string value2: "Test value: " + SingletonType.testProp3;
}

View File

@ -0,0 +1 @@
4:1:Composite Singleton Type SingletonType is not creatable.

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import "singleton"
Item {
property SingletonType test
}

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
import "singleton"
Item {
id: test
property int value1: SingletonType.testProp1;
property string value2: "Test value: " + SingletonType.testProp3;
signal customSignal(SingletonType type)
onCustomSignal: {
type.testProp1 = 99
}
Component.onCompleted: test.customSignal(SingletonType)
}

View File

@ -0,0 +1,2 @@
5:5:Type RegisteredCompositeType unavailable
2:1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import CompositeSingletonTest 1.0
Item {
RegisteredCompositeType { }
}

View File

@ -0,0 +1,2 @@
-1:-1:Type ErrorSingletonType unavailable
-1:-1:qmldir defines type as singleton, but no pragma Singleton found in type ErrorSingletonType.

View File

@ -0,0 +1,8 @@
import QtQuick 2.0
import "singleton/subdir"
Item {
property int value1: ErrorSingletonType.testProp1;
property string value2: "Test value: " + ErrorSingletonType.testProp3;
property variant singletonInstance: ErrorSingletonType;
}

View File

@ -0,0 +1 @@
2:1:singleton types require 2 or 3 arguments, but 1 were provided

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
import "singleton/qmldir-error"
Item {
property int value1: SType2.testProp1;
property string value2: "Test value: " + SType2.testProp3;
}

View File

@ -0,0 +1,8 @@
import QtQuick 2.0
import "http://127.0.0.1:14447/singleton/remote"
Item {
property int value1: RemoteSingletonType2.testProp1;
property string value2: "Test value: " + RemoteSingletonType2.testProp3;
property variant singletonInstance: RemoteSingletonType2;
}

View File

@ -0,0 +1,10 @@
import QtQuick 2.0
import "singleton"
import "singleton/js/jspragma.js" as JsPragmaTest
Item {
id: test
property int value1: SingletonType.testProp1;
property string value2: "Test value: " + JsPragmaTest.value;
}

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import "singleton"
Item {
property variant singleton1: SingletonType;
}

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import "singleton"
Item {
property QtObject singleton2: SingletonType;
}

View File

@ -0,0 +1 @@
2:1:No matching type found, pragma Singleton files cannot be used by QQmlComponent.

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
pragma Singleton
Item {
}

View File

@ -0,0 +1,8 @@
import QtQuick 2.0
import "singleton" as TestNameSpace
Item {
property int value1: TestNameSpace.SingletonType.testProp1;
property string value2: "Test value: " + TestNameSpace.SingletonType.testProp3;
property variant singletonInstance: TestNameSpace.SingletonType;
}

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import "singleton"
Item {
property variant singletonInstance: SingletonType;
}

View File

@ -0,0 +1,14 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 1.0
Item {
property int value1: SingletonType.testProp1;
property string value2: "Test value: " + SingletonType.testProp3;
property variant singletonInstance: SingletonType;
NonSingletonType { id: nonSingleton }
property int value3: nonSingleton.testProp1;
property string value4: "Test value: " + nonSingleton.testProp3;
}

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 1.0
Item {
property variant singletonInstance: SingletonType;
}

View File

@ -0,0 +1,14 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 2.5
Item {
property int value1: SingletonType.testProp1;
property string value2: "Test value: " + SingletonType.testProp3;
property variant singletonInstance: SingletonType;
NonSingletonType { id: nonSingleton }
property int value3: nonSingleton.testProp1;
property string value4: "Test value: " + nonSingleton.testProp3;
}

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 2.5
Item {
property variant singletonInstance: SingletonType;
}

View File

@ -0,0 +1,14 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 2.5 as TestNameSpace
Item {
property int value1: TestNameSpace.SingletonType.testProp1;
property string value2: "Test value: " + TestNameSpace.SingletonType.testProp3;
property variant singletonInstance: TestNameSpace.SingletonType;
TestNameSpace.NonSingletonType { id: nonSingleton }
property int value3: nonSingleton.testProp1;
property string value4: "Test value: " + nonSingleton.testProp3;
}

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
import org.qtproject.SingletonTest 2.5
Item {
property variant singletonInstance: SingletonType;
}

View File

@ -0,0 +1 @@
5:5:Composite Singleton Type SingletonType is not creatable.

View File

@ -0,0 +1,6 @@
import QtQuick 2.0
import "singleton"
Item {
SingletonType {}
}

View File

@ -193,6 +193,23 @@ private slots:
void deepProperty();
void compositeSingletonProperties();
void compositeSingletonSameEngine();
void compositeSingletonDifferentEngine();
void compositeSingletonNonTypeError();
void compositeSingletonQualifiedNamespace();
void compositeSingletonModule();
void compositeSingletonModuleVersioned();
void compositeSingletonModuleQualified();
void compositeSingletonInstantiateError();
void compositeSingletonDynamicPropertyError();
void compositeSingletonDynamicSignal();
void compositeSingletonQmlRegisterTypeError();
void compositeSingletonQmldirNoPragmaError();
void compositeSingletonQmlDirError();
void compositeSingletonRemote();
void compositeSingletonJavaScriptPragma();
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@ -210,6 +227,9 @@ private:
|| userType == (int) QVariant::UInt
|| userType == (int) QVariant::Double;
}
void getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */);
void getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */);
};
#define DETERMINE_ERRORS(errorfile,expected,actual)\
@ -1893,6 +1913,7 @@ void tst_qqmllanguage::reservedWords_data()
QTest::newRow("if") << QByteArray("if");
QTest::newRow("implements") << QByteArray("implements");
QTest::newRow("import") << QByteArray("import");
QTest::newRow("pragma") << QByteArray("pragma");
QTest::newRow("in") << QByteArray("in");
QTest::newRow("instanceof") << QByteArray("instanceof");
QTest::newRow("int") << QByteArray("int");
@ -3194,6 +3215,291 @@ void tst_qqmllanguage::implicitImportsLast()
engine.setImportPathList(defaultImportPathList);
}
void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */)
{
QVERIFY(fileName != 0);
QVERIFY(propertyName != 0);
if (!fileName || !propertyName)
return;
QQmlComponent component(&engine, testFile(fileName));
VERIFY_ERRORS(0);
QObject *object = component.create();
QVERIFY(object != 0);
getSingletonInstance(object, propertyName, result);
}
void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */)
{
QVERIFY(o != 0);
QVERIFY(propertyName != 0);
if (!o || !propertyName)
return;
QVariant variant = o->property(propertyName);
QVERIFY(variant.userType() == qMetaTypeId<QObject *>());
QObject *singleton = NULL;
if (variant.canConvert<QObject*>())
singleton = variant.value<QObject*>();
QVERIFY(singleton != 0);
*result = singleton;
}
void verifyCompositeSingletonPropertyValues(QObject* o, const char* n1, int v1, const char* n2, int v2)
{
QCOMPARE(o->property(n1).userType(), (int)QMetaType::Int);
QCOMPARE(o->property(n1), QVariant(v1));
QCOMPARE(o->property(n2).userType(), (int)QVariant::String);
QString numStr;
QCOMPARE(o->property(n2), QVariant(QString(QLatin1String("Test value: ")).append(numStr.setNum(v2))));
}
// Reads values from a composite singleton type
void tst_qqmllanguage::compositeSingletonProperties()
{
QQmlComponent component(&engine, testFile("singletonTest1.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55);
}
// Checks that the addresses of the composite singletons used in the same
// engine are the same.
void tst_qqmllanguage::compositeSingletonSameEngine()
{
QObject* s1 = NULL;
getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1);
QVERIFY(s1 != 0);
s1->setProperty("testProp2", QVariant(13));
QObject* s2 = NULL;
getSingletonInstance(engine, "singletonTest3.qml", "singleton2", &s2);
QVERIFY(s2 != 0);
QCOMPARE(s2->property("testProp2"), QVariant(13));
QVERIFY(s1 == s2);
}
// Checks that the addresses of the composite singletons used in different
// engines are different.
void tst_qqmllanguage::compositeSingletonDifferentEngine()
{
QQmlEngine e2;
QObject* s1 = NULL;
getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1);
QVERIFY(s1 != 0);
s1->setProperty("testProp2", QVariant(13));
QObject* s2 = NULL;
getSingletonInstance(e2, "singletonTest3.qml", "singleton2", &s2);
QVERIFY(s2 != 0);
QCOMPARE(s2->property("testProp2"), QVariant(25));
QVERIFY(s1 != s2);
}
// pragma Singleton in a non-type qml file fails
void tst_qqmllanguage::compositeSingletonNonTypeError()
{
QQmlComponent component(&engine, testFile("singletonTest4.qml"));
VERIFY_ERRORS("singletonTest4.error.txt");
}
// Loads the singleton using a namespace qualifier
void tst_qqmllanguage::compositeSingletonQualifiedNamespace()
{
QQmlComponent component(&engine, testFile("singletonTest5.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55);
// lets verify that the singleton instance we are using is the same
// when loaded through another file (without namespace!)
QObject *s1 = NULL;
getSingletonInstance(o, "singletonInstance", &s1);
QVERIFY(s1 != 0);
QObject* s2 = NULL;
getSingletonInstance(engine, "singletonTest5a.qml", "singletonInstance", &s2);
QVERIFY(s2 != 0);
QVERIFY(s1 == s2);
}
// Loads a singleton from a module
void tst_qqmllanguage::compositeSingletonModule()
{
engine.addImportPath(testFile("singleton/module"));
QQmlComponent component(&engine, testFile("singletonTest6.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55);
verifyCompositeSingletonPropertyValues(o, "value3", 125, "value4", -55);
// lets verify that the singleton instance we are using is the same
// when loaded through another file
QObject *s1 = NULL;
getSingletonInstance(o, "singletonInstance", &s1);
QVERIFY(s1 != 0);
QObject* s2 = NULL;
getSingletonInstance(engine, "singletonTest6a.qml", "singletonInstance", &s2);
QVERIFY(s2 != 0);
QVERIFY(s1 == s2);
}
// Loads a singleton from a module with a higher version
void tst_qqmllanguage::compositeSingletonModuleVersioned()
{
engine.addImportPath(testFile("singleton/module"));
QQmlComponent component(&engine, testFile("singletonTest7.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55);
verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55);
// lets verify that the singleton instance we are using is the same
// when loaded through another file
QObject *s1 = NULL;
getSingletonInstance(o, "singletonInstance", &s1);
QVERIFY(s1 != 0);
QObject* s2 = NULL;
getSingletonInstance(engine, "singletonTest7a.qml", "singletonInstance", &s2);
QVERIFY(s2 != 0);
QVERIFY(s1 == s2);
}
// Loads a singleton from a module with a qualified namespace
void tst_qqmllanguage::compositeSingletonModuleQualified()
{
engine.addImportPath(testFile("singleton/module"));
QQmlComponent component(&engine, testFile("singletonTest8.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55);
verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55);
// lets verify that the singleton instance we are using is the same
// when loaded through another file
QObject *s1 = NULL;
getSingletonInstance(o, "singletonInstance", &s1);
QVERIFY(s1 != 0);
QObject* s2 = NULL;
getSingletonInstance(engine, "singletonTest8a.qml", "singletonInstance", &s2);
QVERIFY(s2 != 0);
QVERIFY(s1 == s2);
}
// Tries to instantiate a type with a pragma Singleton and fails
void tst_qqmllanguage::compositeSingletonInstantiateError()
{
QQmlComponent component(&engine, testFile("singletonTest9.qml"));
VERIFY_ERRORS("singletonTest9.error.txt");
}
// Having a composite singleton type as dynamic property type fails
// (like C++ singleton)
void tst_qqmllanguage::compositeSingletonDynamicPropertyError()
{
QQmlComponent component(&engine, testFile("singletonTest10.qml"));
VERIFY_ERRORS("singletonTest10.error.txt");
}
// Having a composite singleton type as dynamic signal parameter succeeds
// (like C++ singleton)
void tst_qqmllanguage::compositeSingletonDynamicSignal()
{
QQmlComponent component(&engine, testFile("singletonTest11.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", -55);
}
// Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it.
// This will fail as qmlRegisterType will only instantiate CompositeTypes.
void tst_qqmllanguage::compositeSingletonQmlRegisterTypeError()
{
qmlRegisterType(testFileUrl("singleton/registeredComposite/CompositeType.qml"),
"CompositeSingletonTest", 1, 0, "RegisteredCompositeType");
QQmlComponent component(&engine, testFile("singletonTest12.qml"));
VERIFY_ERRORS("singletonTest12.error.txt");
}
// Qmldir defines a type as a singleton, but the qml file does not have a pragma Singleton.
void tst_qqmllanguage::compositeSingletonQmldirNoPragmaError()
{
QQmlComponent component(&engine, testFile("singletonTest13.qml"));
VERIFY_ERRORS("singletonTest13.error.txt");
}
// Invalid singleton definition in the qmldir file results in an error
void tst_qqmllanguage::compositeSingletonQmlDirError()
{
QQmlComponent component(&engine, testFile("singletonTest14.qml"));
VERIFY_ERRORS("singletonTest14.error.txt");
}
// Load a remote composite singleton type via qmldir that defines the type as a singleton
void tst_qqmllanguage::compositeSingletonRemote()
{
TestHTTPServer server(14447);
server.serveDirectory(dataDirectory());
QQmlComponent component(&engine, testFile("singletonTest15.qml"));
while (component.isLoading())
QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50);
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
verifyCompositeSingletonPropertyValues(o, "value1", 525, "value2", 355);
}
// Load a composite singleton type and a javascript file that has .pragma library
// in it. This will make sure that the javascript .pragma does not get mixed with
// the pragma Singleton changes.
void tst_qqmllanguage::compositeSingletonJavaScriptPragma()
{
QQmlComponent component(&engine, testFile("singletonTest16.qml"));
VERIFY_ERRORS(0);
QObject *o = component.create();
QVERIFY(o != 0);
// The value1 that is read from the SingletonType was changed from 125 to 99
// in compositeSingletonDynamicSignal() above. As the type is a singleton and
// the engine has not been destroyed, we just retrieve the old instance and
// the value is still 99.
verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", 333);
}
QTEST_MAIN(tst_qqmllanguage)

View File

@ -1,4 +1,2 @@
1:12:unexpected token
1:-1:invalid qmldir directive contains too many tokens
2:17:unexpected token
2:-1:invalid qmldir directive contains too many tokens
1:-1:a component declaration requires two or three arguments, but 4 were provided
2:-1:internal types require 2 arguments, but 3 were provided