diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-10-07 05:37:44 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-10-07 05:37:44 (GMT) |
commit | 99573a8e81fcea38c5f68b340068fff266315c03 (patch) | |
tree | 42f71fa7727fa4036096b07f15d2e9ba27ec8ef9 | |
parent | 50a4a8ec76b98cc860de9b6e6aaf25c87e690eed (diff) | |
download | Qt-99573a8e81fcea38c5f68b340068fff266315c03.zip Qt-99573a8e81fcea38c5f68b340068fff266315c03.tar.gz Qt-99573a8e81fcea38c5f68b340068fff266315c03.tar.bz2 |
Make Script an instrinsic type
This allows us to delay the QML load until external script files have
been loaded from the network, and to correctly scope these scripts.
-rw-r--r-- | src/declarative/qml/qml.pri | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler.cpp | 78 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompositetypedata_p.h | 22 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompositetypemanager.cpp | 152 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompositetypemanager_p.h | 6 | ||||
-rw-r--r-- | src/declarative/qml/qmlcontext.cpp | 39 | ||||
-rw-r--r-- | src/declarative/qml/qmlcontext_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlinstruction_p.h | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlmetaproperty.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlparser.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlparser_p.h | 5 | ||||
-rw-r--r-- | src/declarative/qml/qmlscript.cpp (renamed from src/declarative/util/qmlscript.h) | 70 | ||||
-rw-r--r-- | src/declarative/qml/qmlscriptparser.cpp | 51 | ||||
-rw-r--r-- | src/declarative/qml/qmlscriptparser_p.h | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlvme.cpp | 7 | ||||
-rw-r--r-- | src/declarative/util/qmlscript.cpp | 209 | ||||
-rw-r--r-- | src/declarative/util/util.pri | 2 |
18 files changed, 393 insertions, 261 deletions
diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index e46dd3f..a2e2050 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -35,6 +35,7 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlsqldatabase.cpp \ qml/qmetaobjectbuilder.cpp \ qml/qmlwatcher.cpp \ + qml/qmlscript.cpp \ qml/qmlpropertycache.cpp \ qml/qmlintegercache.cpp \ qml/qmltypenamecache.cpp \ diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 4b5c5bf..12e8101 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -669,7 +669,11 @@ bool QmlCompiler::buildObject(Object *obj, const BindingContext &ctxt) if (obj->metatype == &QmlComponent::staticMetaObject) { COMPILE_CHECK(buildComponent(obj, ctxt)); return true; - } + } + + // Build any script blocks for this type + for (int ii = 0; ii < obj->scriptBlockObjects.count(); ++ii) + COMPILE_CHECK(buildScript(obj, obj->scriptBlockObjects.at(ii))); // Object instantiations reset the binding context BindingContext objCtxt(obj); @@ -824,6 +828,15 @@ void QmlCompiler::genObject(QmlParser::Object *obj) output->bytecode << id; } + // Set any script blocks + for (int ii = 0; ii < obj->scriptBlocks.count(); ++ii) { + QmlInstruction script; + script.type = QmlInstruction::StoreScript; + script.line = -1; // ### + script.storeScript.value = output->indexForString(obj->scriptBlocks.at(ii)); + output->bytecode << script; + } + // Begin the class if (obj->parserStatusCast != -1) { QmlInstruction begin; @@ -1000,7 +1013,8 @@ bool QmlCompiler::buildComponent(QmlParser::Object *obj, // Find, check and set the "id" property (if any) Property *idProp = 0; if (obj->properties.count() > 1 || - (obj->properties.count() == 1 && obj->properties.begin().key() != "id")) + (obj->properties.count() == 1 && obj->properties.begin().key() != "id") || + !obj->scriptBlockObjects.isEmpty()) COMPILE_EXCEPTION(obj, "Invalid component specification"); if (obj->properties.count()) @@ -1037,6 +1051,66 @@ bool QmlCompiler::buildComponent(QmlParser::Object *obj, return true; } +bool QmlCompiler::buildScript(QmlParser::Object *obj, QmlParser::Object *script) +{ + QString scriptCode; + + if (script->properties.count() == 1 && + script->properties.begin().key() == QByteArray("source")) { + + Property *source = *script->properties.begin(); + if (script->defaultProperty) + COMPILE_EXCEPTION(source, "Invalid Script block. Specify either the source property or inline script."); + + if (source->value || source->values.count() != 1 || + source->values.at(0)->object || !source->values.at(0)->value.isString()) + COMPILE_EXCEPTION(source, "Invalid Script source value"); + + QString sourceUrl = + output->url.resolved(QUrl(source->values.at(0)->value.asString())).toString(); + + for (int ii = 0; ii < unit->resources.count(); ++ii) { + if (unit->resources.at(ii)->url == sourceUrl) { + scriptCode = QString::fromUtf8(unit->resources.at(ii)->data); + break; + } + } + + } else if (!script->properties.isEmpty()) { + COMPILE_EXCEPTION(*script->properties.begin(), "Properties cannot be set on Script block"); + } else if (script->defaultProperty) { + QmlParser::Location currentLocation; + + for (int ii = 0; ii < script->defaultProperty->values.count(); ++ii) { + Value *v = script->defaultProperty->values.at(ii); + if (v->object || !v->value.isString()) + COMPILE_EXCEPTION(v, "Invalid Script block"); + + if (ii == 0) { + currentLocation = v->location.start; + scriptCode.append(QString(currentLocation.column, QLatin1Char(' '))); + } + + while (currentLocation.line < v->location.start.line) { + scriptCode.append(QLatin1String("\n")); + currentLocation.line++; + currentLocation.column = 0; + } + + scriptCode.append(QString(v->location.start.column - currentLocation.column, QLatin1Char(' '))); + + scriptCode += v->value.asString(); + currentLocation = v->location.end; + currentLocation.column++; + } + } + + if (!scriptCode.isEmpty()) + obj->scriptBlocks.append(scriptCode); + + return true; +} + bool QmlCompiler::buildComponentFromRoot(QmlParser::Object *obj, const BindingContext &ctxt) { diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index fd361fd..1d27342 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -171,6 +171,7 @@ private: bool buildObject(QmlParser::Object *obj, const BindingContext &); + bool buildScript(QmlParser::Object *obj, QmlParser::Object *script); bool buildComponent(QmlParser::Object *obj, const BindingContext &); bool buildSubObject(QmlParser::Object *obj, const BindingContext &); bool buildSignal(QmlParser::Property *prop, QmlParser::Object *obj, diff --git a/src/declarative/qml/qmlcompositetypedata_p.h b/src/declarative/qml/qmlcompositetypedata_p.h index 48c6c2b..fa11137 100644 --- a/src/declarative/qml/qmlcompositetypedata_p.h +++ b/src/declarative/qml/qmlcompositetypedata_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE +class QmlCompositeTypeResource; class QmlCompositeTypeData : public QmlRefCount { public: @@ -101,6 +102,7 @@ public: }; QList<TypeReference> types; + QList<QmlCompositeTypeResource *> resources; // Add or remove p as a waiter. When the QmlCompositeTypeData becomes // ready, the QmlComponentPrivate::typeDataReady() method will be invoked on @@ -122,5 +124,25 @@ private: QmlCompiledData *compiledComponent; }; +class QmlCompositeTypeResource : public QmlRefCount +{ +public: + QmlCompositeTypeResource(); + virtual ~QmlCompositeTypeResource(); + + enum Status { + Invalid, + Complete, + Error, + Waiting + }; + Status status; + + QList<QmlCompositeTypeData *> dependants; + + QString url; + QByteArray data; +}; + #endif // QMLCOMPOSITETYPEDATA_P_H diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp index a99cff0..71b4ef0 100644 --- a/src/declarative/qml/qmlcompositetypemanager.cpp +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -63,6 +63,9 @@ QmlCompositeTypeData::~QmlCompositeTypeData() for (int ii = 0; ii < dependants.count(); ++ii) dependants.at(ii)->release(); + for (int ii = 0; ii < resources.count(); ++ii) + resources.at(ii)->release(); + if (compiledComponent) compiledComponent->release(); @@ -70,6 +73,16 @@ QmlCompositeTypeData::~QmlCompositeTypeData() delete component; } +QmlCompositeTypeResource::QmlCompositeTypeResource() +{ +} + +QmlCompositeTypeResource::~QmlCompositeTypeResource() +{ + for (int ii = 0; ii < dependants.count(); ++ii) + dependants.at(ii)->release(); +} + void QmlCompositeTypeData::addWaiter(QmlComponentPrivate *p) { waiters << p; @@ -142,6 +155,10 @@ QmlCompositeTypeManager::~QmlCompositeTypeManager() (*iter)->release(); iter = components.erase(iter); } + for (Resources::Iterator iter = resources.begin(); iter != resources.end();) { + (*iter)->release(); + iter = resources.erase(iter); + } } QmlCompositeTypeData *QmlCompositeTypeManager::get(const QUrl &url) @@ -181,8 +198,16 @@ void QmlCompositeTypeManager::clearCache() ++iter; } } -} + for (Resources::Iterator iter = resources.begin(); iter != resources.end();) { + if ((*iter)->status != QmlCompositeTypeResource::Waiting) { + (*iter)->release(); + iter = resources.erase(iter); + } else { + ++iter; + } + } +} void QmlCompositeTypeManager::replyFinished() { @@ -215,6 +240,52 @@ void QmlCompositeTypeManager::replyFinished() reply->deleteLater(); } +void QmlCompositeTypeManager::resourceReplyFinished() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + + QmlCompositeTypeResource *resource = resources.value(reply->url().toString()); + Q_ASSERT(resource); + + if (reply->error() != QNetworkReply::NoError) { + + resource->status = QmlCompositeTypeResource::Error; + + } else { + + resource->status = QmlCompositeTypeResource::Complete; + resource->data = reply->readAll(); + + } + + doComplete(resource); + reply->deleteLater(); +} + +void QmlCompositeTypeManager::loadResource(QmlCompositeTypeResource *resource) +{ + QUrl url(resource->url); + + if (url.scheme() == QLatin1String("file")) { + + QFile file(url.toLocalFile()); + if (file.open(QFile::ReadOnly)) { + resource->data = file.readAll(); + resource->status = QmlCompositeTypeResource::Complete; + } else { + resource->status = QmlCompositeTypeResource::Error; + } + + } else { + + QNetworkReply *reply = + engine->networkAccessManager()->get(QNetworkRequest(url)); + QObject::connect(reply, SIGNAL(finished()), + this, SLOT(resourceReplyFinished())); + + } +} + void QmlCompositeTypeManager::loadSource(QmlCompositeTypeData *unit) { QUrl url(unit->imports.baseUrl()); @@ -308,6 +379,15 @@ void QmlCompositeTypeManager::doComplete(QmlCompositeTypeData *unit) } } +void QmlCompositeTypeManager::doComplete(QmlCompositeTypeResource *resource) +{ + for (int ii = 0; ii < resource->dependants.count(); ++ii) { + checkComplete(resource->dependants.at(ii)); + resource->dependants.at(ii)->release(); + } + resource->dependants.clear(); +} + void QmlCompositeTypeManager::checkComplete(QmlCompositeTypeData *unit) { if (unit->status != QmlCompositeTypeData::Waiting) @@ -329,12 +409,33 @@ void QmlCompositeTypeManager::checkComplete(QmlCompositeTypeData *unit) waiting++; } } + for (int ii = 0; ii < unit->resources.count(); ++ii) { + QmlCompositeTypeResource *r = unit->resources.at(ii); + + if (!r) + continue; + + if (r->status == QmlCompositeTypeResource::Error) { + unit->status = QmlCompositeTypeData::Error; + QmlError error; + error.setUrl(unit->imports.baseUrl()); + error.setDescription(QLatin1String("Resource ") + r->url + + QLatin1String(" unavailable")); + unit->errors << error; + doComplete(unit); + return; + } else if (r->status == QmlCompositeTypeData::Waiting) { + waiting++; + } + } + if (!waiting) { unit->status = QmlCompositeTypeData::Complete; doComplete(unit); } } +// ### Check ref counting in here void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) { QList<QmlScriptParser::TypeReference*> types = unit->data.referencedTypes(); @@ -346,12 +447,6 @@ void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) QmlCompositeTypeData::TypeReference ref; - if (typeName == QByteArray("Property") || - typeName == QByteArray("Signal")) { - unit->types << ref; - continue; - } - QUrl url; int majorVersion; int minorVersion; @@ -431,6 +526,49 @@ void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) unit->types << ref; } + QList<QUrl> resourceList = unit->data.referencedResources(); + for (int ii = 0; ii < resourceList.count(); ++ii) { + QUrl url = unit->imports.baseUrl().resolved(resourceList.at(ii)); + + QmlCompositeTypeResource *resource = resources.value(url.toString()); + + if (!resource) { + resource = new QmlCompositeTypeResource; + resource->status = QmlCompositeTypeResource::Waiting; + resource->url = url.toString(); + resources.insert(resource->url, resource); + + loadResource(resource); + } + + switch(resource->status) { + case QmlCompositeTypeResource::Invalid: + case QmlCompositeTypeResource::Error: + unit->status = QmlCompositeTypeData::Error; + { + QmlError error; + error.setUrl(unit->imports.baseUrl()); + error.setDescription(QLatin1String("Resource ") + resource->url + + QLatin1String(" unavailable")); + unit->errors << error; + } + doComplete(unit); + return; + + case QmlCompositeTypeData::Complete: + break; + + case QmlCompositeTypeData::Waiting: + unit->addref(); + resource->dependants << unit; + waiting++; + break; + } + + resource->addref(); + unit->resources << resource; + } + if (waiting) { unit->status = QmlCompositeTypeData::Waiting; } else { diff --git a/src/declarative/qml/qmlcompositetypemanager_p.h b/src/declarative/qml/qmlcompositetypemanager_p.h index 8f16998..843a9cf 100644 --- a/src/declarative/qml/qmlcompositetypemanager_p.h +++ b/src/declarative/qml/qmlcompositetypemanager_p.h @@ -67,6 +67,7 @@ class QmlComponent; class QmlDomDocument; class QmlCompositeTypeData; +class QmlCompositeTypeResource; class QmlCompositeTypeManager : public QObject { @@ -88,19 +89,24 @@ public: private Q_SLOTS: void replyFinished(); + void resourceReplyFinished(); void requestProgress(qint64 received, qint64 total); private: void loadSource(QmlCompositeTypeData *); + void loadResource(QmlCompositeTypeResource *); void compile(QmlCompositeTypeData *); void setData(QmlCompositeTypeData *, const QByteArray &, const QUrl &); void doComplete(QmlCompositeTypeData *); + void doComplete(QmlCompositeTypeResource *); void checkComplete(QmlCompositeTypeData *); QmlEngine *engine; typedef QHash<QString, QmlCompositeTypeData *> Components; Components components; + typedef QHash<QString, QmlCompositeTypeResource *> Resources; + Resources resources; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index a1eb5de..549925f 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -48,6 +48,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qdebug.h> #include <private/qmlbindingoptimizations_p.h> +#include <QtDeclarative/qmlinfo.h> // 6-bits #define MAXIMUM_DEFAULT_OBJECTS 63 @@ -60,6 +61,44 @@ QmlContextPrivate::QmlContextPrivate() { } +void QmlContextPrivate::addScript(const QString &script, QObject *scopeObject) +{ + if (!engine) + return; + + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + QScriptContext *scriptContext = scriptEngine->pushCleanContext(); + scriptContext->pushScope(scriptValue); + + if (scopeObject) + scriptContext->pushScope(enginePriv->objectClass->newQObject(scopeObject)); + + QScriptValue scope = scriptEngine->newObject(); + scriptContext->setActivationObject(scope); + + QScriptValue val = scriptEngine->evaluate(script); + + if (scriptEngine->hasUncaughtException()) { + if (scriptEngine->uncaughtException().isError()){ + QScriptValue exception = scriptEngine->uncaughtException(); + if (!exception.property(QLatin1String("fileName")).toString().isEmpty()){ + qWarning() << exception.property(QLatin1String("fileName")).toString() + << scriptEngine->uncaughtExceptionLineNumber() + << exception.toString(); + + } else { + qmlInfo(scopeObject) << exception.toString(); + } + } + } + + scriptEngine->popContext(); + + scripts.append(scope); +} + void QmlContextPrivate::dump() { dump(0); diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index 22e5895..d18bfda 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -94,6 +94,7 @@ public: QScriptValue scriptValue; QList<QScriptValue> scripts; + void addScript(const QString &script, QObject *scope); QUrl url; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 38b3191..1dcdace 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -118,6 +118,7 @@ public: StoreInterface, /* storeObject */ StoreSignal, /* storeSignal */ + StoreScript, /* storeScript */ // // Unresolved single assignment @@ -238,6 +239,9 @@ public: int value; } storeString; struct { + int value; + } storeScript; + struct { int propertyIndex; int value; } storeUrl; diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index 155b34a..302ce8c 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -564,7 +564,7 @@ QmlMetaProperty::setBinding(QmlAbstractBinding *newBinding) const if (!isProperty() || (type() & Attached) || !d->object) return 0; - d->setBinding(d->object, d->core, newBinding); + return d->setBinding(d->object, d->core, newBinding); } QmlAbstractBinding * diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index 39fe1e2..8c46939 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -82,6 +82,8 @@ QmlParser::Object::~Object() prop->release(); foreach(const DynamicProperty &prop, dynamicProperties) if (prop.defaultValue) prop.defaultValue->release(); + foreach(Object *obj, scriptBlockObjects) + obj->release(); } void Object::setBindingBit(int b) diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index e0579b0..16862eb 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -151,6 +151,8 @@ namespace QmlParser Property *defaultProperty; QHash<QByteArray, Property *> properties; + QList<Object *> scriptBlockObjects; + // Output of the compilation phase (these properties continue to exist // in either the defaultProperty or properties members too) void addValueProperty(Property *); @@ -164,6 +166,9 @@ namespace QmlParser QList<Property *> groupedProperties; QList<Property *> valueTypeProperties; + // Script blocks that were nested under this object + QStringList scriptBlocks; + // The bytes to cast instances by to get to the QmlParserStatus // interface. -1 indicates the type doesn't support this interface. // Set by the QmlCompiler. diff --git a/src/declarative/util/qmlscript.h b/src/declarative/qml/qmlscript.cpp index 4ba4f6b..307d72f 100644 --- a/src/declarative/util/qmlscript.h +++ b/src/declarative/qml/qmlscript.cpp @@ -39,46 +39,44 @@ ** ****************************************************************************/ -#ifndef QMLSCRIPT_H -#define QMLSCRIPT_H +// This is just a dummy file to include the documentation -#include <QtDeclarative/qfxglobal.h> -#include <QtCore/qobject.h> -#include <QtDeclarative/qml.h> +/*! + \qmlclass Script QmlScript + \brief The Script element adds JavaScript snippets. + \ingroup group_utility -QT_BEGIN_HEADER + QmlScript is used to add convenient JavaScript "glue" methods to + your Qt Declarative application or component. While you can have any JavaScript code + within a QmlScript, it is best to limit yourself to defining functions. -QT_BEGIN_NAMESPACE + \qml + Script { + function debugMyComponent() { + print(text.text); + print(otherinterestingitem.property); + } + } + MouseRegion { onClicked: debugMyComponent() } + \endqml -QT_MODULE(Declarative) + \note QmlScript executes JavaScript as soon as it is specified. + When defining a component, this may be before the execution context is + fully specified. As a result some properties or items may not be + accessible. By limiting your JavaScript to defining functions that are + only executed later once the context is fully defined, this problem is + avoided. +*/ -class QmlScriptPrivate; -class Q_DECLARATIVE_EXPORT QmlScript : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QmlScript) +/*! + \qmlproperty string Script::script + \default + JavaScript code to execute. +*/ - Q_PROPERTY(QString script READ script WRITE setScript) - Q_PROPERTY(QUrl source READ source WRITE setSource) - Q_CLASSINFO("DefaultProperty", "script") +/*! + \qmlproperty url Script::source -public: - QmlScript(QObject *parent=0); - - QString script() const; - void setScript(const QString &); - - QUrl source() const; - void setSource(const QUrl &); - -private Q_SLOTS: - void replyFinished(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QmlScript) - -QT_END_HEADER - -#endif + Setting this property causes the Script element to read JavaScript code from + the file specified. +*/ diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index c126830..1c7bf83 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -289,12 +289,26 @@ ProcessAST::defineObjectBinding_helper(AST::UiQualifiedId *propertyName, if (lastTypeDot >= 0) resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/')); - QmlScriptParser::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType); + bool isScript = resolvableObjectType == QLatin1String("Script"); + + if (isScript) { + if (_stateStack.isEmpty() || _stateStack.top().property) { + QmlError error; + error.setDescription(QLatin1String("Invalid use of Script block")); + error.setLine(typeLocation.startLine); + error.setColumn(typeLocation.startColumn); + _parser->_errors << error; + } + } Object *obj = new Object; - obj->type = typeRef->id; - typeRef->refObjects.append(obj); + if (!isScript) { + QmlScriptParser::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType); + obj->type = typeRef->id; + + typeRef->refObjects.append(obj); + } // XXX this doesn't do anything (_scope never builds up) _scope.append(resolvableObjectType); @@ -303,7 +317,11 @@ ProcessAST::defineObjectBinding_helper(AST::UiQualifiedId *propertyName, obj->location = location; - if (propertyCount) { + if (isScript) { + + _stateStack.top().object->scriptBlockObjects.append(obj); + + } else if (propertyCount) { Property *prop = currentProperty(); Value *v = new Value; @@ -385,6 +403,26 @@ Object *ProcessAST::defineObjectBinding(AST::UiQualifiedId *qualifiedId, _stateStack.pop(); // object return obj; + } else if (objectType == QLatin1String("Script")) { + + AST::UiObjectMemberList *it = initializer->members; + for (; it; it = it->next) { + AST::UiScriptBinding *scriptBinding = AST::cast<AST::UiScriptBinding *>(it->member); + if (! scriptBinding) + continue; + + QString propertyName = asString(scriptBinding->qualifiedId); + if (propertyName == QLatin1String("source")) { + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(scriptBinding->statement)) { + AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(stmt->expression); + if (string) { + // We need to add this as a resource + _parser->_refUrls << QUrl(string->value->asString()); + } + } + } + } + } return defineObjectBinding_helper(qualifiedId, objectType, typeLocation, location, initializer); @@ -867,6 +905,11 @@ QList<QmlScriptParser::TypeReference*> QmlScriptParser::referencedTypes() const return _refTypes; } +QList<QUrl> QmlScriptParser::referencedResources() const +{ + return _refUrls; +} + Object *QmlScriptParser::tree() const { return root; diff --git a/src/declarative/qml/qmlscriptparser_p.h b/src/declarative/qml/qmlscriptparser_p.h index d489610..b25d6bf 100644 --- a/src/declarative/qml/qmlscriptparser_p.h +++ b/src/declarative/qml/qmlscriptparser_p.h @@ -102,6 +102,7 @@ public: bool parse(const QByteArray &data, const QUrl &url = QUrl()); QList<TypeReference*> referencedTypes() const; + QList<QUrl> referencedResources() const; QmlParser::Object *tree() const; QList<Import> imports() const; @@ -123,6 +124,7 @@ public: QmlParser::Object *root; QList<Import> _imports; QList<TypeReference*> _refTypes; + QList<QUrl> _refUrls; QString _scriptFile; QmlScriptParserJsASTData *data; }; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 4ba412b..e4eef64 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -564,6 +564,13 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, } break; + case QmlInstruction::StoreScript: + { + QObject *target = stack.top(); + cp->addScript(primitives.at(instr.storeScript.value), target); + } + break; + case QmlInstruction::BeginObject: { QObject *target = stack.top(); diff --git a/src/declarative/util/qmlscript.cpp b/src/declarative/util/qmlscript.cpp deleted file mode 100644 index 5d58f64..0000000 --- a/src/declarative/util/qmlscript.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $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 either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, 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. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtDeclarative/qmlengine.h> -#include <QtDeclarative/qmlcontext.h> -#include <private/qobject_p.h> -#include <QtCore/qfile.h> -#include <QtCore/qdebug.h> -#include <QtScript/qscriptvalue.h> -#include <QtScript/qscriptcontext.h> -#include <QtScript/qscriptengine.h> -#include <private/qmlnullablevalue_p.h> -#include <private/qmlengine_p.h> -#include <private/qmlcontext_p.h> -#include "qmlscript.h" -#include <QNetworkReply> -#include <QNetworkRequest> -#include <QtDeclarative/qmlinfo.h> -#include <private/qfxperf_p.h> - -QT_BEGIN_NAMESPACE - -class QmlScriptPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QmlScript); - -public: - QmlScriptPrivate() : reply(0) {} - - void addScriptToEngine(const QString &, const QString &source=QString()); - - QString script; - QNetworkReply *reply; - QUrl url; -}; - -/*! - \qmlclass Script QmlScript - \brief The Script element adds JavaScript snippets. - \ingroup group_utility - - QmlScript is used to add convenient JavaScript "glue" methods to - your Qt Declarative application or component. While you can have any JavaScript code - within a QmlScript, it is best to limit yourself to defining functions. - - \qml - Script { - function debugMyComponent() { - print(text.text); - print(otherinterestingitem.property); - } - } - MouseRegion { onClicked: debugMyComponent() } - \endqml - - \note QmlScript executes JavaScript as soon as it is specified. - When defining a component, this may be before the execution context is - fully specified. As a result some properties or items may not be - accessible. By limiting your JavaScript to defining functions that are - only executed later once the context is fully defined, this problem is - avoided. -*/ - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,Script,QmlScript) -QmlScript::QmlScript(QObject *parent) : QObject(*(new QmlScriptPrivate), parent) -{ -} - -/*! - \qmlproperty string Script::script - \default - JavaScript code to execute. -*/ -QString QmlScript::script() const -{ - Q_D(const QmlScript); - return d->script; -} - -void QmlScript::setScript(const QString &script) -{ - Q_D(QmlScript); - d->script = script; - d->addScriptToEngine(d->script); -} - -/*! - \qmlproperty url Script::source - - Setting this property causes the Script element to read JavaScript code from - the file specified. -*/ -QUrl QmlScript::source() const -{ - Q_D(const QmlScript); - return d->url; -} - -void QmlScript::setSource(const QUrl &source) -{ - Q_D(QmlScript); - if (d->url == source) - return; - d->url = qmlContext(this)->resolvedUrl(source); - -#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML - if (d->url.scheme() == QLatin1String("file")) { - QFile file(d->url.toLocalFile()); - file.open(QIODevice::ReadOnly); - QByteArray ba = file.readAll(); - d->addScriptToEngine(QString::fromUtf8(ba), file.fileName()); - } else -#endif - { - QNetworkRequest req(d->url); - d->reply = qmlEngine(this)->networkAccessManager()->get(req); - QObject::connect(d->reply, SIGNAL(finished()), - this, SLOT(replyFinished())); - } -} - -void QmlScript::replyFinished() -{ - Q_D(QmlScript); - if (!d->reply->error()) { - QByteArray ba = d->reply->readAll(); - d->addScriptToEngine(QString::fromUtf8(ba), d->url.toString()); - } - d->reply->deleteLater(); - d->reply = 0; -} - -void QmlScriptPrivate::addScriptToEngine(const QString &script, const QString &source) -{ -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxPerfTimer<QFxPerf::AddScript> pt; -#endif - Q_Q(QmlScript); - QmlEngine *engine = qmlEngine(q); - QmlContext *context = qmlContext(q); - QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); - - QScriptContext *scriptContext = scriptEngine->pushCleanContext(); - scriptContext->pushScope(QmlContextPrivate::get(context)->scriptValue); - - QScriptValue scope = scriptEngine->newObject(); - scriptContext->pushScope(scope); - - scriptContext->setActivationObject(scope); - - QScriptValue val = scriptEngine->evaluate(script, source); - if (scriptEngine->hasUncaughtException()) { - if (scriptEngine->uncaughtException().isError()){ - QScriptValue exception = scriptEngine->uncaughtException(); - if (!exception.property(QLatin1String("fileName")).toString().isEmpty()){ - qWarning() << exception.property(QLatin1String("fileName")).toString() - << scriptEngine->uncaughtExceptionLineNumber() - << exception.toString(); - - } else { - qmlInfo(q) << exception.toString(); - } - } - } - - scriptEngine->popContext(); - - context->d_func()->scripts.append(scope); -} - -QT_END_NAMESPACE diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index 41c9019..ec9967c 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -4,7 +4,6 @@ SOURCES += \ util/qperformancelog.cpp \ util/qmlconnection.cpp \ util/qmlpackage.cpp \ - util/qmlscript.cpp \ util/qmlanimation.cpp \ util/qmlsystempalette.cpp \ util/qmlspringfollow.cpp \ @@ -29,7 +28,6 @@ HEADERS += \ util/qperformancelog_p.h \ util/qmlconnection.h \ util/qmlpackage.h \ - util/qmlscript.h \ util/qmlanimation.h \ util/qmlanimation_p.h \ util/qmlsystempalette.h \ |