Skip to content

Commit

Permalink
Implement script module api property get and set
Browse files Browse the repository at this point in the history
This commit adds code for property get and set for script module
APIs, and also splits up the module API unit tests into QObject and
Script (QJSValue) parts.
Related to commit: 3ee8a19

Task-number: QMLNG-33
Task-number: QTBUG-17318
Change-Id: I4aaf5d1cc1d4774dd0f0999f0985439e4d76f0ca
Reviewed-on: http://codereview.qt.nokia.com/1472
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Chris Adams authored and Qt by Nokia committed Aug 30, 2011
1 parent 6cec35c commit be244df
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 61 deletions.
18 changes: 10 additions & 8 deletions src/declarative/qml/qdeclarative.h
Expand Up @@ -420,33 +420,35 @@ Q_DECLARATIVE_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor
Installing a module API into a uri allows developers to provide arbitrary functionality
(methods and properties) in a namespace that doesn't necessarily contain elements.
A module API may be either a QObject or a QScriptValue. Only one module API provider
A module API may be either a QObject or a QJSValue. Only one module API provider
may be registered into any given namespace (combination of \a uri, \a majorVersion and \a minorVersion).
This function should be used to register a module API provider function which returns a QScriptValue as a module API.
This function should be used to register a module API provider function which returns a QJSValue as a module API.
\e NOTE: QJSValue module API properties will \e not trigger binding re-evaluation if changed.
Usage:
\code
// first, define the module API provider function (callback).
static QScriptValue *example_qscriptvalue_module_api_provider(QDeclarativeEngine *engine, QScriptEngine *scriptEngine)
static QJSValue *example_qjsvalue_module_api_provider(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
static int seedValue = 5;
QScriptValue example = scriptEngine->newObject();
QJSValue example = scriptEngine->newObject();
example.setProperty("someProperty", seedValue++);
return example;
}
// second, register the module API provider with QML by calling this function in an initialization function.
...
qmlRegisterModuleApi("Qt.example.qscriptvalueApi", 1, 0, example_qscriptvalue_module_api_provider);
qmlRegisterModuleApi("Qt.example.qjsvalueApi", 1, 0, example_qjsvalue_module_api_provider);
...
\endcode
In order to use the registered module API in QML, you must import the module API.
\qml
import QtQuick 2.0
import Qt.example.qscriptvalueApi 1.0 as ExampleApi
import Qt.example.qjsvalueApi 1.0 as ExampleApi
Item {
id: root
property int someValue: ExampleApi.someProperty
Expand Down Expand Up @@ -474,7 +476,7 @@ inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMi
Installing a module API into a uri allows developers to provide arbitrary functionality
(methods and properties) in a namespace that doesn't necessarily contain elements.
A module API may be either a QObject or a QScriptValue. Only one module API provider
A module API may be either a QObject or a QJSValue. Only one module API provider
may be registered into any given namespace (combination of \a uri, \a majorVersion and \a minorVersion).
This function should be used to register a module API provider function which returns a QObject as a module API.
Expand Down Expand Up @@ -507,7 +509,7 @@ inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMi
};
// second, define the module API provider function (callback).
static QObject *example_qobject_module_api_provider(QDeclarativeEngine *engine, QScriptEngine *scriptEngine)
static QObject *example_qobject_module_api_provider(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
Expand Down
23 changes: 20 additions & 3 deletions src/declarative/qml/v8/qv8typewrapper.cpp
Expand Up @@ -45,6 +45,9 @@
#include <private/qdeclarativeengine_p.h>
#include <private/qdeclarativecontext_p.h>

#include <private/qjsvalue_p.h>
#include <private/qscript_impl_p.h>

QT_BEGIN_NAMESPACE

class QV8TypeResource : public QV8ObjectResource
Expand Down Expand Up @@ -186,6 +189,11 @@ v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property,
if (moduleApi->qobjectApi) {
v8::Handle<v8::Value> rv = v8engine->qobjectWrapper()->getProperty(moduleApi->qobjectApi, propertystring, QV8QObjectWrapper::IgnoreRevision);
return rv;
} else if (moduleApi->scriptApi.isValid()) {
// NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi);
QScopedPointer<QJSValuePrivate> propertyValue(apiprivate->property(property).give());
return propertyValue->asV8Value(v8engine);
} else {
return v8::Handle<v8::Value>();
}
Expand All @@ -212,8 +220,6 @@ v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property,

QV8Engine *v8engine = resource->engine;

// XXX TODO: Implement writes to module API objects

QHashedV8String propertystring(property);

if (resource->type && resource->object) {
Expand All @@ -235,9 +241,20 @@ v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property,
moduleApi->qobjectCallback = 0;
}

if (moduleApi->qobjectApi)
if (moduleApi->qobjectApi) {
v8engine->qobjectWrapper()->setProperty(moduleApi->qobjectApi, propertystring, value,
QV8QObjectWrapper::IgnoreRevision);
} else if (moduleApi->scriptApi.isValid()) {
QScopedPointer<QJSValuePrivate> setvalp(new QJSValuePrivate(v8engine, value));
QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi);
if (apiprivate->propertyFlags(property) & QJSValue::ReadOnly) {
QString error = QLatin1String("Cannot assign to read-only property \"") +
v8engine->toString(property) + QLatin1Char('\"');
v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
} else {
apiprivate->setProperty(property, setvalp.data());
}
}
}
}

Expand Down
@@ -1,15 +1,13 @@
import QtQuick 2.0

import Qt.test 1.0 as QtTest // module API installed into existing uri
import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri
import Qt.test.qobjectApi 1.0 as QtTestQObjectApi // qobject module API installed into new uri
import Qt.test.qobjectApi 1.3 as QtTestMinorVersionQObjectApi // qobject module API installed into existing uri with new minor version
import Qt.test.qobjectApi 2.0 as QtTestMajorVersionQObjectApi // qobject module API installed into existing uri with new major version
import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri

QtObject {
property int existingUriTest: QtTest.qobjectTestProperty
property int scriptTest: QtTestScriptApi.scriptTestProperty
property int qobjectTest: QtTestQObjectApi.qobjectTestProperty
property int qobjectMethodTest: 2
property int qobjectMinorVersionTest: QtTestMinorVersionQObjectApi.qobjectTestProperty
Expand Down
@@ -1,12 +1,10 @@
import QtQuick 2.0

import Qt.test 1.0 as QtTest // module API installed into existing uri
import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri
import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri

QtObject {
property int existingUriTest: QtTest.qobjectTestProperty
property int scriptTest: QtTestScriptApi.scriptTestProperty
property int qobjectParentedTest: QtTestParentedQObjectApi.qobjectTestProperty
}

@@ -1,6 +1,5 @@
import QtQuick 2.0

import Qt.test 1.0 as QtTest // module API installed into existing uri
import Qt.test 1.0 as QtTest // qobject module API installed into existing uri

QtObject {
property int firstProperty: 1
Expand Down
@@ -0,0 +1,6 @@
import QtQuick 2.0
import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri

QtObject {
property int scriptTest: QtTestScriptApi.scriptTestProperty // script module api's only provide properties.
}
@@ -0,0 +1,6 @@
import QtQuick 2.0
import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri

QtObject {
property int scriptTest: QtTestScriptApi.scriptTestProperty
}
@@ -0,0 +1,32 @@
import QtQuick 2.0
import Qt.test.scriptApi 1.0 as QtTestScriptApi
import Qt.test.scriptApi 2.0 as QtTestScriptApi2

QtObject {
property int firstProperty
property int readBack

property int secondProperty
property int unchanged

onFirstPropertyChanged: {
if (QtTestScriptApi.scriptTestProperty != firstProperty) {
QtTestScriptApi.scriptTestProperty = firstProperty;
readBack = QtTestScriptApi.scriptTestProperty;
}
}

onSecondPropertyChanged: {
if (QtTestScriptApi2.scriptTestProperty != secondProperty) {
QtTestScriptApi2.scriptTestProperty = secondProperty;
unchanged = QtTestScriptApi2.scriptTestProperty;
}
}

Component.onCompleted: {
firstProperty = QtTestScriptApi.scriptTestProperty;
readBack = QtTestScriptApi.scriptTestProperty;
secondProperty = QtTestScriptApi2.scriptTestProperty;
unchanged = QtTestScriptApi2.scriptTestProperty;
}
}
16 changes: 16 additions & 0 deletions tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
Expand Up @@ -111,6 +111,21 @@ static QJSValue script_api(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
return v;
}

static QJSValue readonly_script_api(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)

static int testProperty = 42;
QJSValue v = scriptEngine->newObject();
v.setProperty("scriptTestProperty", testProperty++);

// now freeze it so that it's read-only
QJSValue freezeFunction = scriptEngine->evaluate("(function(obj) { return Object.freeze(obj); })");
v = freezeFunction.call(QJSValue(), (QJSValueList() << v));

return v;
}

static QObject *qobject_api(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Expand Down Expand Up @@ -166,6 +181,7 @@ void registerTypes()
qmlRegisterModuleApi("Qt.test",1,0,script_api); // register (script) module API for an existing uri which contains elements
qmlRegisterModuleApi("Qt.test",1,0,qobject_api); // register (qobject) for an existing uri for which another module API was previously regd. Should replace!
qmlRegisterModuleApi("Qt.test.scriptApi",1,0,script_api); // register (script) module API for a uri which doesn't contain elements
qmlRegisterModuleApi("Qt.test.scriptApi",2,0,readonly_script_api); // register (script) module API for a uri which doesn't contain elements - will be made read-only
qmlRegisterModuleApi("Qt.test.qobjectApi",1,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements
qmlRegisterModuleApi("Qt.test.qobjectApi",1,3,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, minor version set
qmlRegisterModuleApi("Qt.test.qobjectApi",2,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, major version set
Expand Down
2 changes: 1 addition & 1 deletion tests/auto/declarative/qdeclarativeecmascript/testtypes.h
Expand Up @@ -945,7 +945,7 @@ class testQObjectApi : public QObject

~testQObjectApi() {}

Q_INVOKABLE int qobjectTestMethod() { m_methodCallCount += 1; return m_methodCallCount; }
Q_INVOKABLE int qobjectTestMethod(int increment = 1) { m_methodCallCount += increment; return m_methodCallCount; }

int qobjectTestProperty() const { return m_testProperty; }
void setQObjectTestProperty(int tp) { m_testProperty = tp; emit qobjectTestPropertyChanged(tp); }
Expand Down

0 comments on commit be244df

Please sign in to comment.