diff options
93 files changed, 5166 insertions, 1755 deletions
diff --git a/demos/declarative/contacts/FieldText.qml b/demos/declarative/contacts/FieldText.qml index d30d4d8..2e8b60d 100644 --- a/demos/declarative/contacts/FieldText.qml +++ b/demos/declarative/contacts/FieldText.qml @@ -9,29 +9,29 @@ Rectangle { property var label: "" onTextChanged: { reset() } signal confirmed - resources: [ - Script { - function edit() { - if (!contacts.mouseGrabbed) { - fieldText.state='editing'; - contacts.mouseGrabbed=true; - } - } - function confirm() { - fieldText.text = textEdit.text; - fieldText.state=''; - contacts.mouseGrabbed=false; - fieldText.confirmed(); - } - function reset() { - textEdit.text = fieldText.text; - fieldText.state=''; - contacts.mouseGrabbed=false; + Script { + + function edit() { + if (!contacts.mouseGrabbed) { + fieldText.state='editing'; + contacts.mouseGrabbed=true; } - } - ] + function confirm() { + fieldText.text = textEdit.text; + fieldText.state=''; + contacts.mouseGrabbed=false; + fieldText.confirmed(); + } + function reset() { + textEdit.text = fieldText.text; + fieldText.state=''; + contacts.mouseGrabbed=false; + } + + } + Image { id: cancelIcon width: 22 diff --git a/demos/declarative/contacts/RemoveButton.qml b/demos/declarative/contacts/RemoveButton.qml index cc858c3..2c3cc9e 100644 --- a/demos/declarative/contacts/RemoveButton.qml +++ b/demos/declarative/contacts/RemoveButton.qml @@ -8,21 +8,20 @@ Rectangle { radius: 5 property var expandedWidth: 230 signal confirmed - resources: [ - Script { - function toggle() { - if (removeButton.state == 'opened') { - removeButton.state = ''; - contacts.mouseGrabbed=false; - } else { - if (!contacts.mouseGrabbed) { - removeButton.state = 'opened'; - contacts.mouseGrabbed=true; - } + Script { + function toggle() { + if (removeButton.state == 'opened') { + removeButton.state = ''; + contacts.mouseGrabbed=false; + } else { + if (!contacts.mouseGrabbed) { + removeButton.state = 'opened'; + contacts.mouseGrabbed=true; } } } - ] + } + Image { id: trashIcon width: 22 diff --git a/demos/declarative/flickr/common/MediaLineEdit.qml b/demos/declarative/flickr/common/MediaLineEdit.qml index 4b21f66..f959bc5 100644 --- a/demos/declarative/flickr/common/MediaLineEdit.qml +++ b/demos/declarative/flickr/common/MediaLineEdit.qml @@ -6,18 +6,18 @@ Item { property string label property string text - width: Math.max(94,label.width + editor.width + 20) + width: Math.max(94,labeltext.width + editor.width + 20) height: buttonImage.height states: [ State { name: "Edit" PropertyChanges { - target: label + target: labeltext text: container.label + ": " } PropertyChanges { - target: label + target: labeltext x: 10 } PropertyChanges { @@ -78,7 +78,7 @@ Item { } Text { - id: label + id: labeltext font.bold: true color: "white" anchors.verticalCenter: container.verticalCenter @@ -93,7 +93,7 @@ Item { selectionColor: "green" width: 0 clip: true - anchors.left: label.right + anchors.left: labeltext.right anchors.verticalCenter: container.verticalCenter } Keys.forwardTo: [(returnKey), (editor)] diff --git a/demos/declarative/samegame/content/samegame.js b/demos/declarative/samegame/content/samegame.js index ba2346f..15bafc4 100755 --- a/demos/declarative/samegame/content/samegame.js +++ b/demos/declarative/samegame/content/samegame.js @@ -21,9 +21,14 @@ function timeStr(msecs) { return ret; } +function getTileSize() +{ + return tileSize; +} + function initBoard() { - for(i = 0; i<maxIndex; i++){ + for(var i = 0; i<maxIndex; i++){ //Delete old blocks if(board[i] != null) board[i].destroy(); @@ -38,16 +43,19 @@ function initBoard() scoreName.forceClose(); dialog.forceClose(); + var a = new Date(); //Initialize Board board = new Array(maxIndex); gameCanvas.score = 0; - for(xIdx=0; xIdx<maxX; xIdx++){ - for(yIdx=0; yIdx<maxY; yIdx++){ + for(var xIdx=0; xIdx<maxX; xIdx++){ + for(var yIdx=0; yIdx<maxY; yIdx++){ board[index(xIdx,yIdx)] = null; createBlock(xIdx,yIdx); } } timer = new Date(); + + print(timer.valueOf() - a.valueOf()); } var fillFound;//Set after a floodFill call to the number of tiles found @@ -55,8 +63,8 @@ var floodBoard;//Set to 1 if the floodFill reaches off that node //NOTE: Be careful with vars named x,y, as the calling object's x,y are still in scope function handleClick(x,y) { - xIdx = Math.floor(x/gameCanvas.tileSize); - yIdx = Math.floor(y/gameCanvas.tileSize); + var xIdx = Math.floor(x/gameCanvas.tileSize); + var yIdx = Math.floor(y/gameCanvas.tileSize); if(xIdx >= maxX || xIdx < 0 || yIdx >= maxY || yIdx < 0) return; if(board[index(xIdx, yIdx)] == null) @@ -102,14 +110,14 @@ function floodFill(xIdx,yIdx,type) function shuffleDown() { //Fall down - for(xIdx=0; xIdx<maxX; xIdx++){ - fallDist = 0; - for(yIdx=maxY-1; yIdx>=0; yIdx--){ + for(var xIdx=0; xIdx<maxX; xIdx++){ + var fallDist = 0; + for(var yIdx=maxY-1; yIdx>=0; yIdx--){ if(board[index(xIdx,yIdx)] == null){ fallDist += 1; }else{ if(fallDist > 0){ - obj = board[index(xIdx,yIdx)]; + var obj = board[index(xIdx,yIdx)]; obj.targetY += fallDist * gameCanvas.tileSize; board[index(xIdx,yIdx+fallDist)] = obj; board[index(xIdx,yIdx)] = null; @@ -140,8 +148,8 @@ function shuffleDown() function victoryCheck() { //awards bonuses for no tiles left - deservesBonus = true; - for(xIdx=maxX-1; xIdx>=0; xIdx--) + var deservesBonus = true; + for(var xIdx=maxX-1; xIdx>=0; xIdx--) if(board[index(xIdx, maxY - 1)] != null) deservesBonus = false; if(deservesBonus) @@ -163,7 +171,7 @@ function floodMoveCheck(xIdx, yIdx, type) return false; if(board[index(xIdx, yIdx)] == null) return false; - myType = board[index(xIdx, yIdx)].type; + var myType = board[index(xIdx, yIdx)].type; if(type == myType) return true; return floodMoveCheck(xIdx + 1, yIdx, myType) || @@ -171,7 +179,7 @@ function floodMoveCheck(xIdx, yIdx, type) } function createBlock(xIdx,yIdx){ - if(component==null) + if(component==null) component = createComponent(tileSrc); // Note that we don't wait for the component to become ready. This will @@ -179,7 +187,7 @@ function createBlock(xIdx,yIdx){ // not be ready immediately. There is a statusChanged signal on the // component you could use if you want to wait to load remote files. if(component.isReady){ - dynamicObject = component.createObject(); + var dynamicObject = component.createObject(); if(dynamicObject == null){ print("error creating block"); print(component.errorsString()); diff --git a/demos/declarative/samegame/samegame.qml b/demos/declarative/samegame/samegame.qml index 709f4e9..ea7c14c 100644 --- a/demos/declarative/samegame/samegame.qml +++ b/demos/declarative/samegame/samegame.qml @@ -5,8 +5,6 @@ Rectangle { id: screen width: 490; height: 720 - Script { source: "content/samegame.js" } - SystemPalette { id: activePalette; colorGroup: Qt.Active } Item { @@ -23,9 +21,11 @@ Rectangle { property int score: 0 property int tileSize: 40 + Script { source: "content/samegame.js" } + z: 20; anchors.centerIn: parent - width: parent.width - (parent.width % tileSize); - height: parent.height - (parent.height % tileSize); + width: parent.width - (parent.width % getTileSize()); + height: parent.height - (parent.height % getTileSize()); MouseRegion { id: gameMR diff --git a/demos/declarative/webbrowser/fieldtext/FieldText.qml b/demos/declarative/webbrowser/fieldtext/FieldText.qml index fe55185..2adfbbf 100644 --- a/demos/declarative/webbrowser/fieldtext/FieldText.qml +++ b/demos/declarative/webbrowser/fieldtext/FieldText.qml @@ -10,7 +10,6 @@ Item { signal cancelled signal startEdit - resources: [ Script { function edit() { @@ -36,7 +35,6 @@ Item { } } - ] Image { id: cancelIcon diff --git a/examples/declarative/layouts/Button.qml b/examples/declarative/layouts/Button.qml index 186512b..44d0c7b 100644 --- a/examples/declarative/layouts/Button.qml +++ b/examples/declarative/layouts/Button.qml @@ -1,31 +1,18 @@ import Qt 4.6 -Rectangle { - id: page - border.color: "black" - color: "steelblue" - radius: 5 - width: pix.width + text.width + 13 - height: pix.height + 10 +Rectangle { border.color: "black"; color: "steelblue"; radius: 5; width: pix.width + textelement.width + 13; height: pix.height + 10; id: page property string text property string icon signal clicked Image { id: pix; x: 5; y:5; source: parent.icon} - - Text { id: text; text: page.text; color: "white"; x:pix.width+pix.x+3; anchors.verticalCenter: pix.verticalCenter;} - - MouseRegion { - id: mr - anchors.fill: parent - onClicked: { parent.focus = true; page.clicked() } - } + Text { id: textelement; text: page.text; color: "white"; x:pix.width+pix.x+3; anchors.verticalCenter: pix.verticalCenter;} + MouseRegion{ id:mr; anchors.fill: parent; onClicked: {parent.focus = true; page.clicked()}} states: - State { - name: "pressed"; when: mr.pressed - PropertyChanges { target:text; x: 5 } - PropertyChanges { target:pix; x:text.x+text.width + 3 } + State{ name:"pressed"; when:mr.pressed + PropertyChanges {target:textelement; x: 5} + PropertyChanges {target:pix; x:textelement.x+textelement.width + 3} } transitions: diff --git a/examples/declarative/tutorials/contacts/1_Drawing_and_Animation/1/RemoveButton.qml b/examples/declarative/tutorials/contacts/1_Drawing_and_Animation/1/RemoveButton.qml deleted file mode 100644 index a8ac7fe..0000000 --- a/examples/declarative/tutorials/contacts/1_Drawing_and_Animation/1/RemoveButton.qml +++ /dev/null @@ -1,11 +0,0 @@ -import Qt 4.6 - -//! [0] -Rectangle { - id: removeButton - width: 30 - height: 30 - color: "red" - radius: 5 -} -//! [0] diff --git a/examples/declarative/webview/content/FieldText.qml b/examples/declarative/webview/content/FieldText.qml index fe55185..2adfbbf 100644 --- a/examples/declarative/webview/content/FieldText.qml +++ b/examples/declarative/webview/content/FieldText.qml @@ -10,7 +10,6 @@ Item { signal cancelled signal startEdit - resources: [ Script { function edit() { @@ -36,7 +35,6 @@ Item { } } - ] Image { id: cancelIcon diff --git a/examples/declarative/xmldata/yahoonews.qml b/examples/declarative/xmldata/yahoonews.qml index bad1d88..6d43f46 100644 --- a/examples/declarative/xmldata/yahoonews.qml +++ b/examples/declarative/xmldata/yahoonews.qml @@ -32,7 +32,7 @@ Rectangle { height: wrapper.height + 10 MouseRegion { anchors.fill: wrapper - onPressed: { delegate.ListView.list.currentIndex = index; } + onPressed: { delegate.ListView.view.currentIndex = index; } onClicked: { if (wrapper.state == 'Details') { wrapper.state = '';} else {wrapper.state = 'Details';} } } Rectangle { diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.cpp index 7586746..2457e71 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.cpp +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.cpp @@ -59,13 +59,8 @@ FunctionExecutable::~FunctionExecutable() delete m_codeBlock; } -JSObject* EvalExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +void EvalExecutable::compile(ExecState*exec, RefPtr<EvalNode> &evalNode, ScopeChainNode* scopeChainNode) { - int errLine; - UString errMsg; - RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); - if (!evalNode) - return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); recordParse(evalNode->features(), evalNode->lineNo(), evalNode->lastLine()); ScopeChain scopeChain(scopeChainNode); @@ -75,7 +70,18 @@ JSObject* EvalExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNod m_evalCodeBlock = new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth()); OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(evalNode.get(), globalObject->debugger(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock)); generator->generate(); - +} + +JSObject* EvalExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + int errLine; + UString errMsg; + RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); + if (!evalNode) + return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); + + compile(exec, evalNode, scopeChainNode); + evalNode->destroyData(); return 0; } diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.h b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.h index 76764f9..92eb5dc 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.h +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Executable.h @@ -176,6 +176,7 @@ namespace JSC { } JSObject* compile(ExecState*, ScopeChainNode*); + void compile(ExecState*, RefPtr<EvalNode> &, ScopeChainNode*); ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*); static PassRefPtr<EvalExecutable> create(ExecState* exec, const SourceCode& source) { return adoptRef(new EvalExecutable(exec, source)); } diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 2769040..a8c3504 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -231,7 +231,8 @@ void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer); QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true; animationsToStart << animation; - startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); + if (!startStopAnimationTimer.isActive()) + startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); } void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) @@ -245,11 +246,12 @@ void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) // this is needed if we unregister an animation while its running if (idx <= currentAnimationIdx) --currentAnimationIdx; - if (animations.isEmpty()) + if (animations.isEmpty() && !startStopAnimationTimer.isActive()) startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); } else { animationsToStart.removeOne(animation); } + QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = false; } diff --git a/src/declarative/fx/qfxgraphicsobjectcontainer.cpp b/src/declarative/fx/qfxgraphicsobjectcontainer.cpp index c0cac6c..5a61d12 100644 --- a/src/declarative/fx/qfxgraphicsobjectcontainer.cpp +++ b/src/declarative/fx/qfxgraphicsobjectcontainer.cpp @@ -43,9 +43,42 @@ #include <QGraphicsObject> #include <QGraphicsWidget> #include <QGraphicsSceneResizeEvent> +#include <private/qfxitem_p.h> QT_BEGIN_NAMESPACE +class QFxGraphicsObjectContainerPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxGraphicsObjectContainer) + +public: + QFxGraphicsObjectContainerPrivate() : QFxItemPrivate(), graphicsObject(0), syncedResize(false) + { } + + void _q_updateSize(); + + void setFiltering(bool on) + { + Q_Q(QFxGraphicsObjectContainer); + if (graphicsObject && graphicsObject->isWidget()) { + if (!on) { + graphicsObject->removeEventFilter(q); + QObject::disconnect(q, SIGNAL(widthChanged()), q, SLOT(_q_updateSize())); + QObject::disconnect(q, SIGNAL(heightChanged()), q, SLOT(_q_updateSize())); + } else { + graphicsObject->installEventFilter(q); + QObject::connect(q, SIGNAL(widthChanged()), q, SLOT(_q_updateSize())); + QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(_q_updateSize())); + } + } + } + + + QGraphicsObject *graphicsObject; + bool syncedResize; +}; + + /*! \qmlclass GraphicsObjectContainer QFxGraphicsObjectContainer \brief The GraphicsObjectContainer element allows you to add QGraphicsObjects into Fluid UI elements. @@ -61,7 +94,7 @@ QML_DEFINE_NOCREATE_TYPE(QGraphicsObject) QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,GraphicsObjectContainer,QFxGraphicsObjectContainer) QFxGraphicsObjectContainer::QFxGraphicsObjectContainer(QFxItem *parent) -: QFxItem(parent), _graphicsObject(0), _syncedResize(false) +: QFxItem(*new QFxGraphicsObjectContainerPrivate, parent) { } @@ -71,7 +104,8 @@ QFxGraphicsObjectContainer::~QFxGraphicsObjectContainer() QGraphicsObject *QFxGraphicsObjectContainer::graphicsObject() const { - return _graphicsObject; + Q_D(const QFxGraphicsObjectContainer); + return d->graphicsObject; } /*! @@ -80,30 +114,45 @@ QGraphicsObject *QFxGraphicsObjectContainer::graphicsObject() const */ void QFxGraphicsObjectContainer::setGraphicsObject(QGraphicsObject *object) { - if (object == _graphicsObject) + Q_D(QFxGraphicsObjectContainer); + if (object == d->graphicsObject) return; - //### what should we do with previously set object? + //### remove previously set item? + + d->setFiltering(false); + + d->graphicsObject = object; - _graphicsObject = object; + if (d->graphicsObject) { + d->graphicsObject->setParentItem(this); - if (_graphicsObject) { - _graphicsObject->setParentItem(this); + if (d->syncedResize && d->graphicsObject->isWidget()) { + QGraphicsWidget *gw = static_cast<QGraphicsWidget*>(d->graphicsObject); + QSizeF gwSize = gw->size(); //### should we use sizeHint? + QSizeF newSize = gwSize; + if (heightValid()) + newSize.setHeight(height()); + if (widthValid()) + newSize.setWidth(width()); + if (gwSize != newSize) + gw->resize(newSize); - if (_syncedResize && _graphicsObject->isWidget()) { - _graphicsObject->installEventFilter(this); - QSizeF newSize = static_cast<QGraphicsWidget*>(_graphicsObject)->size(); //### use sizeHint? - setImplicitWidth(newSize.width()); - setImplicitHeight(newSize.height()); + gwSize = gw->size(); + setImplicitWidth(gwSize.width()); + setImplicitHeight(gwSize.height()); + + d->setFiltering(true); } } } QVariant QFxGraphicsObjectContainer::itemChange(GraphicsItemChange change, const QVariant &value) { + Q_D(QFxGraphicsObjectContainer); if (change == ItemSceneHasChanged) { - QGraphicsObject *o = _graphicsObject; - _graphicsObject = 0; + QGraphicsObject *o = d->graphicsObject; + d->graphicsObject = 0; setGraphicsObject(o); } return QFxItem::itemChange(change, value); @@ -111,9 +160,10 @@ QVariant QFxGraphicsObjectContainer::itemChange(GraphicsItemChange change, const bool QFxGraphicsObjectContainer::eventFilter(QObject *watched, QEvent *e) { - if (watched == _graphicsObject && e->type() == QEvent::GraphicsSceneResize) { - if (_graphicsObject && _graphicsObject->isWidget() && _syncedResize) { - QSizeF newSize = static_cast<QGraphicsWidget*>(_graphicsObject)->size(); + Q_D(QFxGraphicsObjectContainer); + if (watched == d->graphicsObject && e->type() == QEvent::GraphicsSceneResize) { + if (d->graphicsObject && d->graphicsObject->isWidget() && d->syncedResize) { + QSizeF newSize = static_cast<QGraphicsWidget*>(d->graphicsObject)->size(); setImplicitWidth(newSize.width()); setImplicitHeight(newSize.height()); } @@ -142,40 +192,27 @@ bool QFxGraphicsObjectContainer::eventFilter(QObject *watched, QEvent *e) */ bool QFxGraphicsObjectContainer::synchronizedResizing() const { - return _syncedResize; + Q_D(const QFxGraphicsObjectContainer); + return d->syncedResize; } void QFxGraphicsObjectContainer::setSynchronizedResizing(bool on) { - if (on == _syncedResize) + Q_D(QFxGraphicsObjectContainer); + if (on == d->syncedResize) return; - if (_graphicsObject && _graphicsObject->isWidget()) { - if (!on) { - _graphicsObject->removeEventFilter(this); - disconnect(this, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); - disconnect(this, SIGNAL(heightChanged()), this, SLOT(_q_updateSize())); - } - } - - _syncedResize = on; - - if (_graphicsObject && _graphicsObject->isWidget()) { - if (on) { - _graphicsObject->installEventFilter(this); - connect(this, SIGNAL(widthChanged()), this, SLOT(_q_updateSize())); - connect(this, SIGNAL(heightChanged()), this, SLOT(_q_updateSize())); - } - } + d->syncedResize = on; + d->setFiltering(on); } -void QFxGraphicsObjectContainer::_q_updateSize() +void QFxGraphicsObjectContainerPrivate::_q_updateSize() { - if (!_graphicsObject || !_graphicsObject->isWidget() || !_syncedResize) + if (!graphicsObject || !graphicsObject->isWidget() || !syncedResize) return; - QGraphicsWidget *gw = static_cast<QGraphicsWidget*>(_graphicsObject); - const QSizeF newSize(width(), height()); + QGraphicsWidget *gw = static_cast<QGraphicsWidget*>(graphicsObject); + const QSizeF newSize(width, height); gw->resize(newSize); //### will respecting the widgets min/max ever get us in trouble? (all other items always @@ -189,3 +226,5 @@ void QFxGraphicsObjectContainer::_q_updateSize() } QT_END_NAMESPACE + +#include "moc_qfxgraphicsobjectcontainer.cpp" diff --git a/src/declarative/fx/qfxgraphicsobjectcontainer.h b/src/declarative/fx/qfxgraphicsobjectcontainer.h index a8b7c8c..656a7f8 100644 --- a/src/declarative/fx/qfxgraphicsobjectcontainer.h +++ b/src/declarative/fx/qfxgraphicsobjectcontainer.h @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QGraphicsObject; +class QFxGraphicsObjectContainerPrivate; class Q_DECLARATIVE_EXPORT QFxGraphicsObjectContainer : public QFxItem { @@ -74,12 +75,9 @@ protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); bool eventFilter(QObject *watched, QEvent *e); -private Q_SLOTS: - void _q_updateSize(); - private: - QGraphicsObject *_graphicsObject; - bool _syncedResize; + Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QFxGraphicsObjectContainer) }; QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxgridview.cpp b/src/declarative/fx/qfxgridview.cpp index a8b27f4..2d25d56 100644 --- a/src/declarative/fx/qfxgridview.cpp +++ b/src/declarative/fx/qfxgridview.cpp @@ -149,7 +149,7 @@ class QFxGridViewPrivate : public QFxFlickablePrivate public: QFxGridViewPrivate() - : model(0), currentItem(0), tmpCurrent(0), flow(QFxGridView::LeftToRight) + : model(0), currentItem(0), flow(QFxGridView::LeftToRight) , visiblePos(0), visibleIndex(0) , currentIndex(-1) , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1) , highlightComponent(0), highlight(0), trackedItem(0) @@ -298,7 +298,6 @@ public: QList<FxGridItem*> visibleItems; QHash<QFxItem*,int> unrequestedItems; FxGridItem *currentItem; - QFxItem *tmpCurrent; QFxGridView::Flow flow; int visiblePos; int visibleIndex; @@ -640,10 +639,6 @@ void QFxGridViewPrivate::updateCurrent(int modelIndex) return; } - if (tmpCurrent) { - delete tmpCurrent; - tmpCurrent = 0; - } FxGridItem *oldCurrentItem = currentItem; currentIndex = modelIndex; currentItem = createItem(modelIndex); @@ -821,12 +816,8 @@ void QFxGridView::setCurrentIndex(int index) QFxItem *QFxGridView::currentItem() { Q_D(QFxGridView); - if (!d->currentItem) { - // Always return something valid - if (!d->tmpCurrent) - d->tmpCurrent = new QFxItem(viewport()); - return d->tmpCurrent; - } + if (!d->currentItem) + return 0; return d->currentItem->item; } diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp index 1247021..5afd881 100644 --- a/src/declarative/fx/qfxlistview.cpp +++ b/src/declarative/fx/qfxlistview.cpp @@ -170,15 +170,15 @@ class QFxListViewPrivate : public QFxFlickablePrivate public: QFxListViewPrivate() - : model(0), currentItem(0), tmpCurrent(0), orient(Qt::Vertical) + : model(0), currentItem(0), orient(Qt::Vertical) , visiblePos(0), visibleIndex(0) , averageSize(100.0), currentIndex(-1), requestedIndex(-1) , highlightRangeStart(0), highlightRangeEnd(0) , highlightComponent(0), highlight(0), trackedItem(0) , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0), spacing(0.0) + , highlightMoveSpeed(400), highlightResizeSpeed(400) , ownModel(false), wrap(false), autoHighlight(true) , haveHighlightRange(false), strictHighlightRange(false) - , highlightMoveSpeed(400), highlightResizeSpeed(400) {} void init(); @@ -371,7 +371,6 @@ public: QList<FxListItem*> visibleItems; QHash<QFxItem*,int> unrequestedItems; FxListItem *currentItem; - QFxItem *tmpCurrent; Qt::Orientation orient; int visiblePos; int visibleIndex; @@ -752,10 +751,6 @@ void QFxListViewPrivate::updateCurrent(int modelIndex) return; } - if (tmpCurrent) { - delete tmpCurrent; - tmpCurrent = 0; - } FxListItem *oldCurrentItem = currentItem; currentIndex = modelIndex; currentItem = createItem(modelIndex); @@ -991,14 +986,8 @@ void QFxListView::setCurrentIndex(int index) QFxItem *QFxListView::currentItem() { Q_D(QFxListView); - if (!d->currentItem) { - // Always return something valid - if (!d->tmpCurrent) { - d->tmpCurrent = new QFxItem; - d->tmpCurrent->setParent(viewport()); - } - return d->tmpCurrent; - } + if (!d->currentItem) + return 0; return d->currentItem->item; } diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp index b7248ea..943f909 100644 --- a/src/declarative/fx/qfxvisualitemmodel.cpp +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -239,6 +239,10 @@ class QFxVisualDataModelPrivate : public QObjectPrivate public: QFxVisualDataModelPrivate(QmlContext *); + static QFxVisualDataModelPrivate *get(QFxVisualDataModel *m) { + return static_cast<QFxVisualDataModelPrivate *>(QObjectPrivate::get(m)); + } + QGuard<QListModelInterface> m_listModelInterface; QGuard<QAbstractItemModel> m_abstractItemModel; QGuard<QFxVisualDataModel> m_visualItemModel; @@ -363,7 +367,8 @@ class QFxVisualDataModelData : public QObject { Q_OBJECT public: - QFxVisualDataModelData(int index, QFxVisualDataModelPrivate *model); + QFxVisualDataModelData(int index, QFxVisualDataModel *model); + ~QFxVisualDataModelData(); Q_PROPERTY(int index READ index NOTIFY indexChanged) int index() const; @@ -379,7 +384,7 @@ Q_SIGNALS: private: friend class QFxVisualDataModelDataMetaObject; int m_index; - QFxVisualDataModelPrivate *m_model; + QGuard<QFxVisualDataModel> m_model; QFxVisualDataModelDataMetaObject *m_meta; }; @@ -404,15 +409,19 @@ int QFxVisualDataModelDataMetaObject::createProperty(const char *name, const cha QFxVisualDataModelData *data = static_cast<QFxVisualDataModelData *>(object()); - if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) - && data->m_model->m_listAccessor) { - data->m_model->ensureRoles(); - if (data->m_model->m_roleNames.contains(QLatin1String(name))) + if (!data->m_model) + return -1; + + QFxVisualDataModelPrivate *model = QFxVisualDataModelPrivate::get(data->m_model); + + if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) { + model->ensureRoles(); + if (model->m_roleNames.contains(QLatin1String(name))) return QmlOpenMetaObject::createProperty(name, type); } else { - data->m_model->ensureRoles(); + model->ensureRoles(); const QLatin1String sname(name); - if (data->m_model->m_roleNames.contains(sname)) + if (model->m_roleNames.contains(sname)) return QmlOpenMetaObject::createProperty(name, type); } return -1; @@ -425,45 +434,48 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro QFxVisualDataModelData *data = static_cast<QFxVisualDataModelData *>(object()); + + Q_ASSERT(data->m_model); + QFxVisualDataModelPrivate *model = QFxVisualDataModelPrivate::get(data->m_model); + QString name = QLatin1String(prop.name()); - if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) - && data->m_model->m_listAccessor) { + if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) { if (name == QLatin1String("modelData")) { - if (data->m_model->m_listAccessor->type() == QmlListAccessor::Instance) { - QObject *object = data->m_model->m_listAccessor->at(0).value<QObject*>(); + if (model->m_listAccessor->type() == QmlListAccessor::Instance) { + QObject *object = model->m_listAccessor->at(0).value<QObject*>(); return object->metaObject()->property(1).read(object); // the first property after objectName } - return data->m_model->m_listAccessor->at(data->m_index); + return model->m_listAccessor->at(data->m_index); } else { // return any property of a single object instance. - QObject *object = data->m_model->m_listAccessor->at(0).value<QObject*>(); + QObject *object = model->m_listAccessor->at(0).value<QObject*>(); return object->property(prop.name()); } - } else if (data->m_model->m_listModelInterface) { - data->m_model->ensureRoles(); - QHash<QString,int>::const_iterator it = data->m_model->m_roleNames.find(name); - if (it != data->m_model->m_roleNames.end()) { + } else if (model->m_listModelInterface) { + model->ensureRoles(); + QHash<QString,int>::const_iterator it = model->m_roleNames.find(name); + if (it != model->m_roleNames.end()) { roles.append(*it); - QHash<int,QVariant> values = data->m_model->m_listModelInterface->data(data->m_index, QList<int>() << *it); + QHash<int,QVariant> values = model->m_listModelInterface->data(data->m_index, QList<int>() << *it); if (values.isEmpty()) return QVariant(); else return values.value(*it); - } else if (data->m_model->m_roles.count() == 1 && name == QLatin1String("modelData")) { + } else if (model->m_roles.count() == 1 && name == QLatin1String("modelData")) { //for compatability with other lists, assign modelData if there is only a single role - QHash<int,QVariant> values = data->m_model->m_listModelInterface->data(data->m_index, QList<int>() << data->m_model->m_roles.first()); + QHash<int,QVariant> values = model->m_listModelInterface->data(data->m_index, QList<int>() << model->m_roles.first()); if (values.isEmpty()) return QVariant(); else return *values.begin(); } - } else if (data->m_model->m_abstractItemModel) { - data->m_model->ensureRoles(); - QHash<QString,int>::const_iterator it = data->m_model->m_roleNames.find(name); - if (it != data->m_model->m_roleNames.end()) { + } else if (model->m_abstractItemModel) { + model->ensureRoles(); + QHash<QString,int>::const_iterator it = model->m_roleNames.find(name); + if (it != model->m_roleNames.end()) { roles.append(*it); - QModelIndex index = data->m_model->m_abstractItemModel->index(data->m_index, 0); - return data->m_model->m_abstractItemModel->data(index, *it); + QModelIndex index = model->m_abstractItemModel->index(data->m_index, 0); + return model->m_abstractItemModel->data(index, *it); } } Q_ASSERT(!"Can never be reached"); @@ -471,12 +483,16 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro } QFxVisualDataModelData::QFxVisualDataModelData(int index, - QFxVisualDataModelPrivate *model) + QFxVisualDataModel *model) : m_index(index), m_model(model), m_meta(new QFxVisualDataModelDataMetaObject(this)) { } +QFxVisualDataModelData::~QFxVisualDataModelData() +{ +} + int QFxVisualDataModelData::index() const { return m_index; @@ -752,7 +768,7 @@ QFxItem *QFxVisualDataModel::item(int index, const QByteArray &viewId, bool comp QmlContext *ccontext = d->m_context; if (!ccontext) ccontext = qmlContext(this); QmlContext *ctxt = new QmlContext(ccontext); - QFxVisualDataModelData *data = new QFxVisualDataModelData(index, d); + QFxVisualDataModelData *data = new QFxVisualDataModelData(index, this); ctxt->setContextProperty(QLatin1String("model"), data); ctxt->addDefaultObject(data); nobj = d->m_delegate->beginCreate(ctxt); @@ -821,7 +837,7 @@ QVariant QFxVisualDataModel::evaluate(int index, const QString &expression, QObj QmlContext *ccontext = d->m_context; if (!ccontext) ccontext = qmlContext(this); QmlContext *ctxt = new QmlContext(ccontext); - QFxVisualDataModelData *data = new QFxVisualDataModelData(index, d); + QFxVisualDataModelData *data = new QFxVisualDataModelData(index, this); ctxt->addDefaultObject(data); QmlExpression e(ctxt, expression, objectContext); e.setTrackChange(false); diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 8349e29..a2e2050 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -34,7 +34,16 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlxmlhttprequest.cpp \ qml/qmlsqldatabase.cpp \ qml/qmetaobjectbuilder.cpp \ - qml/qmlwatcher.cpp + qml/qmlwatcher.cpp \ + qml/qmlscript.cpp \ + qml/qmlpropertycache.cpp \ + qml/qmlintegercache.cpp \ + qml/qmltypenamecache.cpp \ + qml/qmlobjectscriptclass.cpp \ + qml/qmlcontextscriptclass.cpp \ + qml/qmlglobalscriptclass.cpp \ + qml/qmlvaluetypescriptclass.cpp \ + qml/qmltypenamescriptclass.cpp HEADERS += qml/qmlparser_p.h \ qml/qmlinstruction_p.h \ @@ -86,7 +95,15 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlxmlhttprequest_p.h \ qml/qmlsqldatabase_p.h \ qml/qmetaobjectbuilder_p.h \ - qml/qmlwatcher_p.h + qml/qmlwatcher_p.h \ + qml/qmlpropertycache_p.h \ + qml/qmlintegercache_p.h \ + qml/qmltypenamecache_p.h \ + qml/qmlobjectscriptclass_p.h \ + qml/qmlcontextscriptclass_p.h \ + qml/qmlglobalscriptclass_p.h \ + qml/qmlvaluetypescriptclass_p.h \ + qml/qmltypenamescriptclass_p.h # for qtscript debugger contains(QT_CONFIG, scripttools):QT += scripttools diff --git a/src/declarative/qml/qmlbinding.cpp b/src/declarative/qml/qmlbinding.cpp index 454369b..3a34f46 100644 --- a/src/declarative/qml/qmlbinding.cpp +++ b/src/declarative/qml/qmlbinding.cpp @@ -57,11 +57,16 @@ QT_BEGIN_NAMESPACE QML_DEFINE_NOCREATE_TYPE(QmlBinding); -QmlBindingPrivate::QmlBindingPrivate() +QmlBindingData::QmlBindingData() : updating(false), enabled(false) { } +QmlBindingPrivate::QmlBindingPrivate() +: QmlExpressionPrivate(new QmlBindingData) +{ +} + QmlBinding::QmlBinding(void *data, QmlRefCount *rc, QObject *obj, QmlContext *ctxt, QObject *parent) : QmlExpression(ctxt, data, rc, obj, *new QmlBindingPrivate) { @@ -81,7 +86,7 @@ QmlBinding::~QmlBinding() void QmlBinding::setTarget(const QmlMetaProperty &prop) { Q_D(QmlBinding); - d->property = prop; + d->bindingData()->property = prop; update(); } @@ -89,7 +94,7 @@ void QmlBinding::setTarget(const QmlMetaProperty &prop) QmlMetaProperty QmlBinding::property() const { Q_D(const QmlBinding); - return d->property; + return d->bindingData()->property; } void QmlBinding::update() @@ -99,45 +104,41 @@ void QmlBinding::update() #ifdef Q_ENABLE_PERFORMANCE_LOG QFxPerfTimer<QFxPerf::BindableValueUpdate> bu; #endif - if (!d->enabled) + QmlBindingData *data = d->bindingData(); + + if (!data->enabled) return; - if (!d->updating) { - d->updating = true; + data->addref(); - if (d->property.propertyCategory() == QmlMetaProperty::Bindable) { + if (!data->updating) { + data->updating = true; - int idx = d->property.coreIndex(); + if (data->property.propertyCategory() == QmlMetaProperty::Bindable) { + + int idx = data->property.coreIndex(); Q_ASSERT(idx != -1); void *a[1]; QmlBinding *t = this; a[0] = (void *)&t; - QMetaObject::metacall(d->property.object(), + QMetaObject::metacall(data->property.object(), QMetaObject::WriteProperty, idx, a); } else { QVariant value = this->value(); - if (value.type() == QVariant::String) { - QmlMetaType::StringConverter con = QmlMetaType::customStringConverter(d->property.propertyType()); - if (con) - value = con(value.toString()); - } - - if (d->property.propertyType() == QVariant::Vector3D && - value.type() == QVariant::String) { - value = qVariantFromValue(QmlStringConverters::vector3DFromString(value.toString())); - } - - d->property.write(value, QmlMetaProperty::Binding); + data->property.write(value, QmlMetaProperty::Binding); } - d->updating = false; + data->updating = false; } else { - qmlInfo(d->property.object()) << "Binding loop detected for property" << d->property.name(); + qmlInfo(data->property.object()) << "Binding loop detected for property" + << data->property.name(); } + + data->release(); } void QmlBinding::valueChanged() @@ -148,30 +149,30 @@ void QmlBinding::valueChanged() void QmlBinding::setEnabled(bool e) { Q_D(QmlBinding); - d->enabled = e; + d->bindingData()->enabled = e; setTrackChange(e); + QmlAbstractBinding::setEnabled(e); + if (e) { - addToObject(d->property.object()); + addToObject(d->bindingData()->property.object()); update(); } else { removeFromObject(); } - - QmlAbstractBinding::setEnabled(e); } int QmlBinding::propertyIndex() { Q_D(QmlBinding); - return d->property.coreIndex(); + return d->bindingData()->property.coreIndex(); } bool QmlBinding::enabled() const { Q_D(const QmlBinding); - return d->enabled; + return d->bindingData()->enabled; } QString QmlBinding::expression() const diff --git a/src/declarative/qml/qmlbinding.h b/src/declarative/qml/qmlbinding.h index 675917d..e3a297c 100644 --- a/src/declarative/qml/qmlbinding.h +++ b/src/declarative/qml/qmlbinding.h @@ -73,6 +73,7 @@ public: private: friend class QmlDeclarativeData; friend class QmlMetaProperty; + friend class QmlMetaPropertyPrivate; friend class QmlVME; QObject *m_object; diff --git a/src/declarative/qml/qmlbinding_p.h b/src/declarative/qml/qmlbinding_p.h index 963e2c1..2c0c6b9 100644 --- a/src/declarative/qml/qmlbinding_p.h +++ b/src/declarative/qml/qmlbinding_p.h @@ -59,11 +59,10 @@ QT_BEGIN_NAMESPACE -class QmlBindingPrivate : public QmlExpressionPrivate +class QmlBindingData : public QmlExpressionData { - Q_DECLARE_PUBLIC(QmlBinding) public: - QmlBindingPrivate(); + QmlBindingData(); bool updating:1; bool enabled:1; @@ -71,6 +70,16 @@ public: QmlMetaProperty property; }; +class QmlBindingPrivate : public QmlExpressionPrivate +{ + Q_DECLARE_PUBLIC(QmlBinding) +public: + QmlBindingPrivate(); + + QmlBindingData *bindingData() { return static_cast<QmlBindingData *>(data); } + const QmlBindingData *bindingData() const { return static_cast<const QmlBindingData *>(data); } +}; + QT_END_NAMESPACE #endif // QMLBINDING_P_H diff --git a/src/declarative/qml/qmlcompileddata.cpp b/src/declarative/qml/qmlcompileddata.cpp index b2e2d40..8c3c355 100644 --- a/src/declarative/qml/qmlcompileddata.cpp +++ b/src/declarative/qml/qmlcompileddata.cpp @@ -150,6 +150,7 @@ int QmlCompiledData::indexForLocation(const QmlParser::LocationSpan &l) } QmlCompiledData::QmlCompiledData() +: importCache(0) { } @@ -159,6 +160,14 @@ QmlCompiledData::~QmlCompiledData() if (types.at(ii).ref) types.at(ii).ref->release(); } + + for (int ii = 0; ii < propertyCaches.count(); ++ii) + propertyCaches.at(ii)->release(); + + if (importCache) + importCache->release(); + + qDeleteAll(programs); } QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt, const QBitField &bindings) const diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 02cd813..12e8101 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -633,7 +633,7 @@ void QmlCompiler::compileTree(Object *tree) init.line = 0; init.init.bindingsSize = compileState.bindings.count(); init.init.parserStatusSize = compileState.parserStatusCount; - init.init.idSize = compileState.ids.count(); + init.init.contextCache = genContextCache(); output->bytecode << init; genObject(tree); @@ -644,10 +644,11 @@ void QmlCompiler::compileTree(Object *tree) output->bytecode << def; output->imports = unit->imports; + output->importCache = output->imports.cache(engine); Q_ASSERT(tree->metatype); static_cast<QMetaObject &>(output->root) = *tree->metaObject(); - if (!tree->metadata.isEmpty()) + if (!tree->metadata.isEmpty()) QmlEnginePrivate::get(engine)->registerCompositeType(output); } @@ -668,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); @@ -807,7 +812,9 @@ void QmlCompiler::genObject(QmlParser::Object *obj) meta.line = -1; meta.storeMeta.data = output->indexForByteArray(obj->metadata); meta.storeMeta.aliasData = output->indexForByteArray(obj->synthdata); - meta.storeMeta.slotData = -1; + meta.storeMeta.propertyCache = output->propertyCaches.count(); + // ### Surely the creation of this property cache could be more efficient + output->propertyCaches << QmlPropertyCache::create(engine, obj->metaObject()); output->bytecode << meta; } @@ -821,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; @@ -962,7 +978,7 @@ void QmlCompiler::genComponent(QmlParser::Object *obj) init.type = QmlInstruction::Init; init.init.bindingsSize = compileState.bindings.count(); init.init.parserStatusSize = compileState.parserStatusCount; - init.init.idSize = compileState.ids.count(); + init.init.contextCache = genContextCache(); init.line = obj->location.start.line; output->bytecode << init; @@ -997,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()) @@ -1034,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) { @@ -2261,6 +2338,22 @@ void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, output->bytecode << store; } +int QmlCompiler::genContextCache() +{ + if (compileState.ids.count() == 0) + return -1; + + QmlIntegerCache *cache = new QmlIntegerCache(engine); + + for (QHash<QString, QmlParser::Object *>::ConstIterator iter = compileState.ids.begin(); + iter != compileState.ids.end(); + ++iter) + cache->add(iter.key(), (*iter)->idIndex); + + output->contextCaches.append(cache); + return output->contextCaches.count() - 1; +} + bool QmlCompiler::completeComponentBuild() { for (int ii = 0; ii < compileState.aliasingObjects.count(); ++ii) { @@ -2296,7 +2389,10 @@ bool QmlCompiler::completeComponentBuild() expression = rewriteBinding(expression); quint32 length = expression.length(); + quint32 pc = output->programs.length(); + output->programs.append(0); binding.compiledData = + QByteArray((const char *)&pc, sizeof(quint32)) + QByteArray((const char *)&length, sizeof(quint32)) + QByteArray((const char *)expression.constData(), expression.length() * sizeof(QChar)); diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 00637e3..1d27342 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -62,6 +62,9 @@ #include <private/qmlparser_p.h> #include <private/qmlengine_p.h> #include <private/qbitfield_p.h> +#include <private/qmlpropertycache_p.h> +#include <private/qmlintegercache_p.h> +#include <private/qmltypenamecache_p.h> QT_BEGIN_NAMESPACE @@ -69,6 +72,7 @@ class QmlEngine; class QmlComponent; class QmlContext; +class QScriptProgram; class QmlCompiledData : public QmlRefCount { public: @@ -78,6 +82,7 @@ public: QByteArray name; QUrl url; QmlEnginePrivate::Imports imports; + QmlTypeNameCache *importCache; struct TypeReference { @@ -98,6 +103,7 @@ public: int index; int type; }; + QAbstractDynamicMetaObject root; QList<QString> primitives; QList<float> floatData; @@ -106,6 +112,9 @@ public: QList<QByteArray> datas; QList<QmlParser::Location> locations; QList<QmlInstruction> bytecode; + QList<QScriptProgram *> programs; + QList<QmlPropertyCache *> propertyCaches; + QList<QmlIntegerCache *> contextCaches; void dumpInstructions(); private: @@ -162,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, @@ -227,7 +237,7 @@ private: QmlParser::Property *prop, QmlParser::Object *obj, QmlParser::Property *valueTypeProperty = 0); - + int genContextCache(); int componentTypeRef(); @@ -253,12 +263,10 @@ private: struct ComponentCompileState { ComponentCompileState() - : parserStatusCount(0), savedObjects(0), - pushedProperties(0), root(0) {} + : parserStatusCount(0), pushedProperties(0), root(0) {} QHash<QString, QmlParser::Object *> ids; QHash<int, QmlParser::Object *> idIndexes; int parserStatusCount; - int savedObjects; int pushedProperties; QHash<QmlParser::Value *, BindingReference> bindings; diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index fedc2da..5b1cbeb 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -517,7 +517,8 @@ QmlComponentPrivate::beginCreate(QmlContext *context, const QBitField &bindings) static_cast<QmlContextPrivate *>(QObjectPrivate::get(context)); QmlContext *ctxt = new QmlContext(context, 0, true); static_cast<QmlContextPrivate*>(ctxt->d_func())->url = cc->url; - static_cast<QmlContextPrivate*>(ctxt->d_func())->imports = cc->imports; + static_cast<QmlContextPrivate*>(ctxt->d_func())->imports = cc->importCache; + cc->importCache->addref(); QmlVME vme; QObject *rv = vme.run(ctxt, cc, start, count, bindings); diff --git a/src/declarative/qml/qmlcomponentjs_p.h b/src/declarative/qml/qmlcomponentjs_p.h index 0f56766..3213929 100644 --- a/src/declarative/qml/qmlcomponentjs_p.h +++ b/src/declarative/qml/qmlcomponentjs_p.h @@ -69,14 +69,14 @@ class Q_DECLARATIVE_EXPORT QmlComponentJS : public QmlComponent { Q_OBJECT Q_DECLARE_PRIVATE(QmlComponentJS) - friend class QmlEngine; -public: - QmlComponentJS(QmlEngine *, const QUrl &url, QObject *parent = 0); - QmlComponentJS(QmlEngine *, QObject *parent=0); Q_PROPERTY(bool isNull READ isNull NOTIFY isNullChanged) Q_PROPERTY(bool isReady READ isReady NOTIFY isReadyChanged) Q_PROPERTY(bool isError READ isError NOTIFY isErrorChanged) Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged) + friend class QmlEngine; +public: + QmlComponentJS(QmlEngine *, const QUrl &url, QObject *parent = 0); + QmlComponentJS(QmlEngine *, QObject *parent=0); Q_INVOKABLE QScriptValue createObject(); Q_INVOKABLE QString errorsString() const; 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 0632ef7..f6795aa 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 @@ -55,11 +56,49 @@ QT_BEGIN_NAMESPACE QmlContextPrivate::QmlContextPrivate() -: parent(0), engine(0), isInternal(false), notifyIndex(-1), - highPriorityCount(0), expressions(0), idValues(0), idValueCount(0) +: parent(0), engine(0), isInternal(false), propertyNames(0), notifyIndex(-1), + highPriorityCount(0), imports(0), expressions(0), idValues(0), idValueCount(0) { } +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); @@ -103,16 +142,7 @@ void QmlContextPrivate::init() //set scope chain QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); - QScriptValue scopeObj = - scriptEngine->newObject(QmlEnginePrivate::get(engine)->contextClass, scriptEngine->newVariant(QVariant::fromValue((QObject*)q))); - //### no longer need to push global object once we switch to JSC (test with objects added to globalObject) - //if (parent) - // scopeChain = parent->d_func()->scopeChain; - if (!parent) - scopeChain.append(scriptEngine->globalObject()); - else - scopeChain = parent->d_func()->scopeChain; - scopeChain.prepend(scopeObj); + scriptValue = QmlEnginePrivate::get(engine)->contextClass->newContext(q); } void QmlContextPrivate::addDefaultObject(QObject *object, Priority priority) @@ -304,6 +334,12 @@ QmlContext::~QmlContext() d->contextObjects.clear(); delete [] d->idValues; + + if (d->propertyNames) + d->propertyNames->release(); + + if (d->imports) + d->imports->release(); } void QmlContextPrivate::invalidateEngines() @@ -361,13 +397,16 @@ void QmlContext::setContextProperty(const QString &name, const QVariant &value) QObject *o = QmlMetaType::toQObject(value); setContextProperty(name, o); } else { - QHash<QString, int>::ConstIterator iter = d->propertyNames.find(name); - if(iter == d->propertyNames.end()) { - d->propertyNames.insert(name, d->idValueCount + d->propertyValues.count()); + + if (!d->propertyNames) d->propertyNames = new QmlIntegerCache(d->engine); + + int idx = d->propertyNames->value(name); + if (idx == -1) { + d->propertyNames->add(name, d->idValueCount + d->propertyValues.count()); d->propertyValues.append(value); } else { - d->propertyValues[*iter] = value; - QMetaObject::activate(this, *iter + d->notifyIndex, 0); + d->propertyValues[idx] = value; + QMetaObject::activate(this, idx + d->notifyIndex, 0); } } } @@ -380,15 +419,18 @@ void QmlContextPrivate::setIdProperty(const QString &name, int idx, notifyIndex = q->metaObject()->methodCount(); } - propertyNames.insert(name, idx); idValues[idx].priv = this; idValues[idx] = obj; } -void QmlContextPrivate::setIdPropertyCount(int count) +void QmlContextPrivate::setIdPropertyData(QmlIntegerCache *data) { - idValues = new ContextGuard[count]; - idValueCount = count; + Q_ASSERT(!propertyNames); + propertyNames = data; + propertyNames->addref(); + + idValueCount = data->count(); + idValues = new ContextGuard[idValueCount]; } /*! @@ -402,14 +444,15 @@ void QmlContext::setContextProperty(const QString &name, QObject *value) if (d->notifyIndex == -1) d->notifyIndex = this->metaObject()->methodCount(); - QHash<QString, int>::ConstIterator iter = d->propertyNames.find(name); - if(iter == d->propertyNames.end()) { - d->propertyNames.insert(name, d->idValueCount + d->propertyValues.count()); + if (!d->propertyNames) d->propertyNames = new QmlIntegerCache(d->engine); + int idx = d->propertyNames->value(name); + + if (idx == -1) { + d->propertyNames->add(name, d->idValueCount + d->propertyValues.count()); d->propertyValues.append(QVariant::fromValue(value)); } else { - int idx = *iter; - d->propertyValues[*iter] = QVariant::fromValue(value); - QMetaObject::activate(this, *iter + d->notifyIndex, 0); + d->propertyValues[idx] = QVariant::fromValue(value); + QMetaObject::activate(this, idx + d->notifyIndex, 0); } } diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index b305408..d18bfda 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -61,6 +61,8 @@ #include <QtCore/qset.h> #include <private/qguard_p.h> #include <private/qmlengine_p.h> +#include <private/qmlintegercache_p.h> +#include <private/qmltypenamecache_p.h> QT_BEGIN_NAMESPACE @@ -82,17 +84,21 @@ public: QmlEngine *engine; bool isInternal; - QHash<QString, int> propertyNames; + QmlIntegerCache *propertyNames; QList<QVariant> propertyValues; int notifyIndex; QObjectList defaultObjects; int highPriorityCount; - QScriptValueList scopeChain; + QScriptValue scriptValue; + + QList<QScriptValue> scripts; + void addScript(const QString &script, QObject *scope); QUrl url; - QmlEnginePrivate::Imports imports; + + QmlTypeNameCache *imports; void init(); @@ -125,9 +131,13 @@ public: ContextGuard *idValues; int idValueCount; void setIdProperty(const QString &, int, QObject *); - void setIdPropertyCount(int); + void setIdPropertyData(QmlIntegerCache *); void destroyed(ContextGuard *); + static QmlContextPrivate *get(QmlContext *context) { + return static_cast<QmlContextPrivate *>(QObjectPrivate::get(context)); + } + // Only used for debugging QList<QPointer<QObject> > instances; }; diff --git a/src/declarative/qml/qmlcontextscriptclass.cpp b/src/declarative/qml/qmlcontextscriptclass.cpp new file mode 100644 index 0000000..6d2c58c --- /dev/null +++ b/src/declarative/qml/qmlcontextscriptclass.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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 "qmlcontextscriptclass_p.h" +#include <private/qmlengine_p.h> +#include <private/qmlcontext_p.h> +#include <private/qmltypenamescriptclass_p.h> + +QT_BEGIN_NAMESPACE + +struct ContextData : public QScriptDeclarativeClass::Object { + ContextData(QmlContext *c) : context(c) {} + QGuard<QmlContext> context; +}; + +/* + The QmlContextScriptClass handles property access for a QmlContext + via QtScript. + */ +QmlContextScriptClass::QmlContextScriptClass(QmlEngine *bindEngine) +: QScriptDeclarativeClass(QmlEnginePrivate::getScriptEngine(bindEngine)), engine(bindEngine), + lastData(0), lastPropertyIndex(-1), lastDefaultObject(-1) +{ +} + +QmlContextScriptClass::~QmlContextScriptClass() +{ +} + +QScriptValue QmlContextScriptClass::newContext(QmlContext *context) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new ContextData(context)); +} + +QmlContext *QmlContextScriptClass::contextFromValue(const QScriptValue &v) +{ + if (scriptClass(v) != this) + return 0; + + ContextData *data = (ContextData *)object(v); + return data->context; +} + +#include <QDebug> +QScriptClass::QueryFlags +QmlContextScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(flags); + + lastContext = 0; + lastData = 0; + lastPropertyIndex = -1; + lastDefaultObject = -1; + + QmlContext *bindContext = ((ContextData *)object)->context.data(); + if (!bindContext) + return 0; + + while (bindContext) { + QScriptClass::QueryFlags rv = queryProperty(bindContext, name, flags); + if (rv) return rv; + bindContext = bindContext->parentContext(); + } + + return 0; +} + +QScriptClass::QueryFlags +QmlContextScriptClass::queryProperty(QmlContext *bindContext, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + QmlContextPrivate *cp = QmlContextPrivate::get(bindContext); + + lastPropertyIndex = cp->propertyNames?cp->propertyNames->value(name):-1; + if (lastPropertyIndex != -1) { + lastContext = bindContext; + return QScriptClass::HandlesReadAccess; + } + + if (cp->imports) { + QmlTypeNameCache::Data *data = cp->imports->data(name); + + if (data) { + lastData = data; + lastContext = bindContext; + return QScriptClass::HandlesReadAccess; + } + } + + for (int ii = 0; ii < cp->defaultObjects.count(); ++ii) { + QScriptClass::QueryFlags rv = + ep->objectClass->queryProperty(cp->defaultObjects.at(ii), name, flags, 0); + + if (rv) { + lastDefaultObject = ii; + lastContext = bindContext; + return rv; + } + } + + for (int ii = 0; ii < cp->scripts.count(); ++ii) { + lastFunction = QScriptDeclarativeClass::function(cp->scripts.at(ii), name); + if (lastFunction.isValid()) { + lastContext = bindContext; + return QScriptClass::HandlesReadAccess; + } + } + return 0; +} + +QScriptValue QmlContextScriptClass::property(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + + QmlContext *bindContext = lastContext; + Q_ASSERT(bindContext); + + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + QmlContextPrivate *cp = QmlContextPrivate::get(bindContext); + + + if (lastData) { + + if (lastData->type) + return ep->typeNameClass->newObject(cp->defaultObjects.at(0), lastData->type); + else + return ep->typeNameClass->newObject(cp->defaultObjects.at(0), lastData->typeNamespace); + + } else if (lastPropertyIndex != -1) { + + QScriptValue rv; + if (lastPropertyIndex < cp->idValueCount) { + rv = ep->objectClass->newQObject(cp->idValues[lastPropertyIndex].data()); + } else { + QVariant value = cp->propertyValues.at(lastPropertyIndex); + rv = ep->scriptValueFromVariant(value); + } + + ep->capturedProperties << + QmlEnginePrivate::CapturedProperty(bindContext, -1, lastPropertyIndex + cp->notifyIndex); + + return rv; + } else if(lastDefaultObject != -1) { + + // Default object property + return ep->objectClass->property(cp->defaultObjects.at(lastDefaultObject), name); + + } else { + + return lastFunction; + + } +} + +void QmlContextScriptClass::setProperty(Object *object, const Identifier &name, + const QScriptValue &value) +{ + Q_ASSERT(lastDefaultObject != -1); + + QmlContext *bindContext = lastContext; + Q_ASSERT(bindContext); + + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + QmlContextPrivate *cp = QmlContextPrivate::get(bindContext); + + ep->objectClass->setProperty(cp->defaultObjects.at(lastDefaultObject), name, value); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcontextscriptclass_p.h b/src/declarative/qml/qmlcontextscriptclass_p.h new file mode 100644 index 0000000..761a115 --- /dev/null +++ b/src/declarative/qml/qmlcontextscriptclass_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLCONTEXTSCRIPTCLASS_P_H +#define QMLCONTEXTSCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtScript/qscriptclass.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <private/qmltypenamecache_p.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QmlContext; +class QmlContextScriptClass : public QScriptDeclarativeClass +{ +public: + QmlContextScriptClass(QmlEngine *); + ~QmlContextScriptClass(); + + QScriptValue newContext(QmlContext *); + + QmlContext *contextFromValue(const QScriptValue &); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual QScriptValue property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + +private: + QScriptClass::QueryFlags queryProperty(QmlContext *, const Identifier &, + QScriptClass::QueryFlags flags); + + QmlEngine *engine; + + QmlContext *lastContext; + QmlTypeNameCache::Data *lastData; + int lastPropertyIndex; + int lastDefaultObject; + QScriptValue lastFunction; + + uint m_id; +}; + +QT_END_NAMESPACE + +#endif // QMLCONTEXTSCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qmldeclarativedata_p.h b/src/declarative/qml/qmldeclarativedata_p.h index ade961f..f6ecc3d 100644 --- a/src/declarative/qml/qmldeclarativedata_p.h +++ b/src/declarative/qml/qmldeclarativedata_p.h @@ -54,12 +54,14 @@ // #include <private/qobject_p.h> +#include <QtScript/qscriptvalue.h> QT_BEGIN_NAMESPACE class QmlCompiledData; class QmlAbstractBinding; class QmlContext; +class QmlPropertyCache; class QmlDeclarativeData : public QDeclarativeData { public: @@ -85,6 +87,9 @@ public: QHash<int, QObject *> *attachedProperties; + QScriptValue scriptValue; + QmlPropertyCache *propertyCache; + static QmlDeclarativeData *get(const QObject *object, bool create = false) { QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index f34d790..ccdf6cf 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -41,11 +41,13 @@ #undef QT3_SUPPORT // don't want it here - it just causes bugs (which is why we removed it) -#include <QMetaProperty> +#include <QtCore/qmetaobject.h> #include <private/qmlengine_p.h> #include <private/qmlcontext_p.h> #include <private/qobject_p.h> #include <private/qmlcompiler_p.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <private/qmlglobalscriptclass_p.h> #ifdef QT_SCRIPTTOOLS_LIB #include <QScriptEngineDebugger> @@ -83,6 +85,7 @@ #include <private/qmlstringconverters_p.h> #include <private/qmlxmlhttprequest_p.h> #include <private/qmlsqldatabase_p.h> +#include <private/qmltypenamescriptclass_p.h> #ifdef Q_OS_WIN // for %APPDATA% #include "qt_windows.h" @@ -121,7 +124,7 @@ static QString userLocalDataPath(const QString& app) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentExpression(0), - isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), + isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0), nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), scriptEngine(this), rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1) { @@ -148,6 +151,14 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) qtObject.setProperty(QLatin1String("lighter"), scriptEngine.newFunction(QmlEnginePrivate::lighter, 1)); qtObject.setProperty(QLatin1String("darker"), scriptEngine.newFunction(QmlEnginePrivate::darker, 1)); qtObject.setProperty(QLatin1String("tint"), scriptEngine.newFunction(QmlEnginePrivate::tint, 2)); + + scriptEngine.globalObject().setProperty(QLatin1String("createQmlObject"), + scriptEngine.newFunction(QmlEnginePrivate::createQmlObject, 1)); + scriptEngine.globalObject().setProperty(QLatin1String("createComponent"), + scriptEngine.newFunction(QmlEnginePrivate::createComponent, 1)); + + //scriptEngine.globalObject().setScriptClass(new QmlGlobalScriptClass(&scriptEngine)); + globalClass = new QmlGlobalScriptClass(&scriptEngine); } QmlEnginePrivate::~QmlEnginePrivate() @@ -177,6 +188,9 @@ QmlEnginePrivate::~QmlEnginePrivate() clear(parserStatus[ii]); for(QHash<int, QmlCompiledData*>::ConstIterator iter = m_compositeTypes.constBegin(); iter != m_compositeTypes.constEnd(); ++iter) (*iter)->release(); + for(QHash<const QMetaObject *, QmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter) + (*iter)->release(); + } void QmlEnginePrivate::clear(SimpleList<QmlAbstractBinding> &bvs) @@ -212,11 +226,6 @@ void QmlEnginePrivate::init() } #endif - scriptEngine.globalObject().setProperty(QLatin1String("createQmlObject"), - scriptEngine.newFunction(QmlEnginePrivate::createQmlObject, 1)); - scriptEngine.globalObject().setProperty(QLatin1String("createComponent"), - scriptEngine.newFunction(QmlEnginePrivate::createComponent, 1)); - if (QCoreApplication::instance()->thread() == q->thread() && QmlEngineDebugServer::isDebuggingEnabled()) { qmlEngineDebugServer(); @@ -232,203 +241,6 @@ QmlEnginePrivate::CapturedProperty::CapturedProperty(const QmlMetaProperty &p) { } -struct QmlTypeNameBridge -{ - QObject *object; - QmlType *type; - QmlEnginePrivate::ImportedNamespace *ns; -}; -Q_DECLARE_METATYPE(QmlTypeNameBridge); - -struct QmlValueTypeReference { - QmlValueType *type; - QGuard<QObject> object; - int property; -}; -Q_DECLARE_METATYPE(QmlValueTypeReference); - -//////////////////////////////////////////////////////////////////// -QScriptClass::QueryFlags -QmlEnginePrivate::queryContext(const QString &propName, uint *id, - QmlContext *bindContext) -{ - resolveData.safetyCheckId++; - *id = resolveData.safetyCheckId; - resolveData.clear(); - - QHash<QString, int>::Iterator contextProperty = - bindContext->d_func()->propertyNames.find(propName); - - if (contextProperty != bindContext->d_func()->propertyNames.end()) { - - resolveData.context = bindContext; - resolveData.contextIndex = *contextProperty; - - return QScriptClass::HandlesReadAccess; - } - - QmlType *type = 0; ImportedNamespace *ns = 0; - if (currentExpression && bindContext == currentExpression->context() && - propName.at(0).isUpper() && resolveType(bindContext->d_func()->imports, propName.toUtf8(), &type, 0, 0, 0, &ns)) { - - if (type || ns) { - // Must be either an attached property, or an enum - resolveData.object = bindContext->d_func()->defaultObjects.first(); - resolveData.type = type; - resolveData.ns = ns; - return QScriptClass::HandlesReadAccess; - } - - } - - QScriptClass::QueryFlags rv = 0; - for (int ii = 0; !rv && ii < bindContext->d_func()->defaultObjects.count(); ++ii) { - rv = queryObject(propName, id, - bindContext->d_func()->defaultObjects.at(ii)); - } - - return rv; -} - -QScriptValue QmlEnginePrivate::propertyContext(const QScriptString &name, uint id) -{ - Q_ASSERT(id == resolveData.safetyCheckId); - - if (resolveData.type || resolveData.ns) { - QmlTypeNameBridge tnb = { - resolveData.object, - resolveData.type, - resolveData.ns - }; - return scriptEngine.newObject(typeNameClass, scriptEngine.newVariant(qVariantFromValue(tnb))); - } else if (resolveData.context) { - QmlContext *bindContext = resolveData.context; - QmlContextPrivate *contextPrivate = bindContext->d_func(); - int index = resolveData.contextIndex; - - QScriptValue rv; - if (index < contextPrivate->idValueCount) { - rv = scriptEngine.newObject(objectClass, scriptEngine.newVariant(QVariant::fromValue(contextPrivate->idValues[index].data()))); - } else { - QVariant value = contextPrivate->propertyValues.at(index); - if (QmlMetaType::isObject(value.userType())) { - rv = scriptEngine.newObject(objectClass, scriptEngine.newVariant(value)); - } else { - rv = scriptEngine.newVariant(value); - } - } - capturedProperties << QmlEnginePrivate::CapturedProperty(bindContext, -1, index + contextPrivate->notifyIndex); - return rv; - - } else { - - return propertyObject(name, resolveData.object, id); - - } - - return QScriptValue(); -} - -void QmlEnginePrivate::setPropertyContext(const QScriptValue &value, uint id) -{ - // As context properties cannot be written, we can assume that the - // write is a object property write - setPropertyObject(value, id); -} - -void QmlEnginePrivate::setPropertyObject(const QScriptValue &value, uint id) -{ - Q_ASSERT(id == resolveData.safetyCheckId); - Q_Q(QmlEngine); - - resolveData.property.write(QmlScriptClass::toVariant(q, value)); -} - -QScriptClass::QueryFlags -QmlEnginePrivate::queryObject(const QString &propName, - uint *id, QObject *obj) -{ - resolveData.safetyCheckId++; - *id = resolveData.safetyCheckId; - resolveData.clear(); - - QScriptClass::QueryFlags rv = 0; - - QmlContext *ctxt = QmlEngine::contextForObject(obj); - if (!ctxt) - ctxt = rootContext; - QmlMetaProperty prop(obj, propName, ctxt); - - if (prop.type() == QmlMetaProperty::Invalid) { - QPair<const QMetaObject *, QString> key = - qMakePair(obj->metaObject(), propName); - bool isFunction = false; - if (functionCache.contains(key)) { - isFunction = functionCache.value(key); - } else { - QScriptValue sobj = scriptEngine.newQObject(obj); - QScriptValue func = sobj.property(propName); - isFunction = func.isFunction(); - functionCache.insert(key, isFunction); - } - - if (isFunction) { - resolveData.object = obj; - resolveData.isFunction = true; - rv |= QScriptClass::HandlesReadAccess; - } - } else { - resolveData.object = obj; - resolveData.property = prop; - - rv |= QScriptClass::HandlesReadAccess; - if (prop.isWritable()) - rv |= QScriptClass::HandlesWriteAccess; - } - - return rv; -} - -QScriptValue QmlEnginePrivate::propertyObject(const QScriptString &propName, - QObject *obj, uint id) -{ - Q_ASSERT(id == resolveData.safetyCheckId); - Q_ASSERT(resolveData.object); - - if (resolveData.isFunction) { - // ### Optimize - QScriptValue sobj = scriptEngine.newQObject(obj); - QScriptValue func = sobj.property(propName); - return func; - } else { - const QmlMetaProperty &prop = resolveData.property; - - if (prop.needsChangedNotifier()) - capturedProperties << CapturedProperty(prop); - - int propType = prop.propertyType(); - if (propType < QVariant::UserType && valueTypes[propType]) { - QmlValueTypeReference ref; - ref.type = valueTypes[propType]; - ref.object = obj; - ref.property = prop.coreIndex(); - return scriptEngine.newObject(valueTypeClass, scriptEngine.newVariant(QVariant::fromValue(ref))); - } - - QVariant var = prop.read(); - QObject *varobj = (propType < QVariant::UserType)?0:QmlMetaType::toQObject(var); - if (!varobj) - varobj = qvariant_cast<QObject *>(var); - if (varobj) { - return scriptEngine.newObject(objectClass, scriptEngine.newVariant(QVariant::fromValue(varobj))); - } else { - return qScriptValueFromValue(&scriptEngine, var); - } - } - - return QScriptValue(); -} - /*! \class QmlEngine \brief The QmlEngine class provides an environment for instantiating QML components. @@ -666,7 +478,7 @@ QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool cre QmlDeclarativeData::QmlDeclarativeData(QmlContext *ctxt) : context(ctxt), bindings(0), bindingBitsSize(0), bindingBits(0), outerContext(0), lineNumber(0), columnNumber(0), deferredComponent(0), - deferredIdx(0), attachedProperties(0) + deferredIdx(0), attachedProperties(0), propertyCache(0) { } @@ -691,6 +503,9 @@ void QmlDeclarativeData::destroyed(QObject *object) if (bindingBits) free(bindingBits); + if (propertyCache) + propertyCache->release(); + delete this; } @@ -719,9 +534,10 @@ void QmlDeclarativeData::setBindingBit(QObject *obj, int bit) bindingBits = (quint32 *)realloc(bindingBits, arraySize * sizeof(quint32)); + memset(bindingBits + oldArraySize, - sizeof(quint32) * (arraySize - oldArraySize), - 0x00); + 0x00, + sizeof(quint32) * (arraySize - oldArraySize)); bindingBitsSize = arraySize * 32; } @@ -740,8 +556,8 @@ void QmlDeclarativeData::setBindingBit(QObject *obj, int bit) QScriptValue QmlEnginePrivate::qmlScriptObject(QObject* object, QmlEngine* engine) { - QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); - return scriptEngine->newObject(engine->d_func()->objectClass, scriptEngine->newQObject(object, QScriptEngine::AutoOwnership)); + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + return enginePriv->objectClass->newQObject(object); } /*! @@ -857,7 +673,7 @@ QScriptValue QmlEnginePrivate::createQmlObject(QScriptContext *ctxt, QScriptEngi QUrl url; if(ctxt->argumentCount() > 2) url = QUrl(ctxt->argument(2).toString()); - QObject *parentArg = ctxt->argument(1).data().toQObject(); + QObject *parentArg = activeEnginePriv->objectClass->toQObject(ctxt->argument(1)); QmlContext *qmlCtxt = qmlContext(parentArg); url = qmlCtxt->resolvedUrl(url); QmlComponent component(activeEngine, qml.toUtf8(), url); @@ -1083,346 +899,63 @@ QScriptValue QmlEnginePrivate::tint(QScriptContext *ctxt, QScriptEngine *engine) return qScriptValueFromValue(engine, qVariantFromValue(finalColor)); } -QmlScriptClass::QmlScriptClass(QmlEngine *bindengine) -: QScriptClass(QmlEnginePrivate::getScriptEngine(bindengine)), - engine(bindengine) + +QScriptValue QmlEnginePrivate::scriptValueFromVariant(const QVariant &val) { + if (QmlMetaType::isObject(val.userType())) { + QObject *rv = *(QObject **)val.constData(); + return objectClass->newQObject(rv); + } else { + return qScriptValueFromValue(&scriptEngine, val); + } } -QVariant QmlScriptClass::toVariant(QmlEngine *engine, const QScriptValue &val) +QVariant QmlEnginePrivate::scriptValueToVariant(const QScriptValue &val) { - QmlEnginePrivate *ep = - static_cast<QmlEnginePrivate *>(QObjectPrivate::get(engine)); + QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(val); + if (dc == objectClass) + return QVariant::fromValue(objectClass->toQObject(val)); + else if (dc == contextClass) + return QVariant(); QScriptClass *sc = val.scriptClass(); if (!sc) { return val.toVariant(); - } else if (sc == ep->contextClass) { + } else if (sc == valueTypeClass) { + return valueTypeClass->toVariant(val); + } else { return QVariant(); - } else if (sc == ep->objectClass) { - return QVariant::fromValue(val.data().toQObject()); - } else if (sc == ep->valueTypeClass) { - QmlValueTypeReference ref = - qvariant_cast<QmlValueTypeReference>(val.data().toVariant()); - - if (!ref.object) - return QVariant(); - - QMetaProperty p = ref.object->metaObject()->property(ref.property); - return p.read(ref.object); } - - return QVariant(); -} - -///////////////////////////////////////////////////////////// -/* - The QmlContextScriptClass handles property access for a QmlContext - via QtScript. - */ -QmlContextScriptClass::QmlContextScriptClass(QmlEngine *bindEngine) - : QmlScriptClass(bindEngine) -{ -} - -QmlContextScriptClass::~QmlContextScriptClass() -{ } -QScriptClass::QueryFlags -QmlContextScriptClass::queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id) -{ - Q_UNUSED(flags); - QmlContext *bindContext = - static_cast<QmlContext*>(object.data().toQObject()); - - QString propName = name.toString(); - - QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); - return ep->queryContext(propName, id, bindContext); -} - -QScriptValue QmlContextScriptClass::property(const QScriptValue &object, - const QScriptString &name, - uint id) -{ - Q_UNUSED(object); - - QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); - return ep->propertyContext(name, id); -} - -void QmlContextScriptClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value) -{ - Q_UNUSED(object); - Q_UNUSED(name); - - QmlEnginePrivate::get(engine)->setPropertyContext(value, id); -} - -///////////////////////////////////////////////////////////// -QmlTypeNameScriptClass::QmlTypeNameScriptClass(QmlEngine *engine) -: QmlScriptClass(engine), object(0), type(0) -{ -} - -QmlTypeNameScriptClass::~QmlTypeNameScriptClass() +QmlScriptClass::QmlScriptClass(QmlEngine *bindengine) +: QScriptClass(QmlEnginePrivate::getScriptEngine(bindengine)), + engine(bindengine) { } -QmlTypeNameScriptClass::QueryFlags -QmlTypeNameScriptClass::queryProperty(const QScriptValue &scriptObject, - const QScriptString &name, - QueryFlags flags, uint *id) +QVariant QmlScriptClass::toVariant(QmlEngine *engine, const QScriptValue &val) { - Q_UNUSED(flags); - - QmlTypeNameBridge bridge = - qvariant_cast<QmlTypeNameBridge>(scriptObject.data().toVariant()); - - object = 0; - type = 0; - QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); - - if (bridge.ns) { - QmlType *type = 0; - ep->resolveTypeInNamespace(bridge.ns, name.toString().toUtf8(), - &type, 0, 0, 0); - if (type) { - object = bridge.object; - this->type = type; - return HandlesReadAccess; - } else { - return 0; - } + QmlEnginePrivate *ep = + static_cast<QmlEnginePrivate *>(QObjectPrivate::get(engine)); - } else { - Q_ASSERT(bridge.type); - QString strName = name.toString(); - if (strName.at(0).isUpper()) { - // Must be an enum - // ### Optimize - const char *enumName = strName.toUtf8().constData(); - const QMetaObject *metaObject = bridge.type->baseMetaObject(); - for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - int value = e.keyToValue(enumName); - if (value != -1) { - enumValue = value; - return HandlesReadAccess; - } - } - return 0; - } else { - // Must be an attached property - this->object = qmlAttachedPropertiesObjectById(bridge.type->index(), bridge.object); - if (!this->object) - return 0; - return ep->queryObject(strName, id, this->object); - } - } -} + QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(val); + if (dc == ep->objectClass) + return QVariant::fromValue(ep->objectClass->toQObject(val)); + else if (dc == ep->contextClass) + return QVariant(); -QScriptValue QmlTypeNameScriptClass::property(const QScriptValue &, - const QScriptString &propName, - uint id) -{ - QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); - if (type) { - QmlTypeNameBridge tnb = { object, type, 0 }; - return ep->scriptEngine.newObject(ep->typeNameClass, ep->scriptEngine.newVariant(qVariantFromValue(tnb))); - } else if (object) { - return ep->propertyObject(propName, object, id); - } else { - return QScriptValue(enumValue); + QScriptClass *sc = val.scriptClass(); + if (!sc) { + return val.toVariant(); + } else if (sc == ep->valueTypeClass) { + return ep->valueTypeClass->toVariant(val); } -} - -///////////////////////////////////////////////////////////// -QmlValueTypeScriptClass::QmlValueTypeScriptClass(QmlEngine *bindEngine) -: QmlScriptClass(bindEngine) -{ -} - -QmlValueTypeScriptClass::~QmlValueTypeScriptClass() -{ -} - -QmlValueTypeScriptClass::QueryFlags -QmlValueTypeScriptClass::queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id) -{ - Q_UNUSED(flags); - QmlValueTypeReference ref = - qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); - - if (!ref.object) - return 0; - QByteArray propName = name.toString().toUtf8(); - - int idx = ref.type->metaObject()->indexOfProperty(propName.constData()); - if (idx == -1) - return 0; - *id = idx; - - QMetaProperty prop = ref.object->metaObject()->property(idx); - - QmlValueTypeScriptClass::QueryFlags rv = - QmlValueTypeScriptClass::HandlesReadAccess; - if (prop.isWritable()) - rv |= QmlValueTypeScriptClass::HandlesWriteAccess; - - return rv; -} - -QScriptValue QmlValueTypeScriptClass::property(const QScriptValue &object, - const QScriptString &name, - uint id) -{ - Q_UNUSED(name); - QmlValueTypeReference ref = - qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); - - if (!ref.object) - return QScriptValue(); - - ref.type->read(ref.object, ref.property); - - QMetaProperty p = ref.type->metaObject()->property(id); - QVariant rv = p.read(ref.type); - - return static_cast<QmlEnginePrivate *>(QObjectPrivate::get(engine))->scriptEngine.newVariant(rv); -} - -void QmlValueTypeScriptClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value) -{ - Q_UNUSED(name); - QmlValueTypeReference ref = - qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); - - if (!ref.object) - return; - - QVariant v = QmlScriptClass::toVariant(engine, value); - - ref.type->read(ref.object, ref.property); - QMetaProperty p = ref.type->metaObject()->property(id); - p.write(ref.type, v); - ref.type->write(ref.object, ref.property); + return QVariant(); } ///////////////////////////////////////////////////////////// -/* - The QmlObjectScriptClass handles property access for QObjects - via QtScript. It is also used to provide a more useful API in - QtScript for QML. - */ - -QScriptValue QmlObjectToString(QScriptContext *context, QScriptEngine *engine) -{ - QObject* obj = context->thisObject().data().toQObject(); - QString ret = QLatin1String("Qml Object, "); - if(obj){ - //###Should this be designer or developer details? Dev for now. - //TODO: Can we print the id too? - ret += QLatin1String("\""); - ret += obj->objectName(); - ret += QLatin1String("\" "); - ret += QLatin1String(obj->metaObject()->className()); - ret += QLatin1String("(0x"); - ret += QString::number((quintptr)obj,16); - ret += QLatin1String(")"); - }else{ - ret += QLatin1String("null"); - } - return engine->newVariant(ret); -} - -QScriptValue QmlObjectDestroy(QScriptContext *context, QScriptEngine *engine) -{ - QObject* obj = context->thisObject().data().toQObject(); - if(obj){ - int delay = 0; - if(context->argumentCount() > 0) - delay = context->argument(0).toInt32(); - QTimer::singleShot(delay, obj, SLOT(deleteLater())); - //### Should this be delayed as well? - context->thisObject().setData(QScriptValue(engine, 0)); - } - return engine->nullValue(); -} - -QmlObjectScriptClass::QmlObjectScriptClass(QmlEngine *bindEngine) - : QmlScriptClass(bindEngine) -{ - engine = bindEngine; - QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(bindEngine); - prototypeObject = scriptEngine->newObject(); - prototypeObject.setProperty(QLatin1String("toStr"),//TODO: Why won't toString work? - scriptEngine->newFunction(QmlObjectToString)); - prototypeObject.setProperty(QLatin1String("destroy"), - scriptEngine->newFunction(QmlObjectDestroy)); -} - -QmlObjectScriptClass::~QmlObjectScriptClass() -{ -} - -QScriptValue QmlObjectScriptClass::prototype() const -{ - return prototypeObject; -} - -QScriptClass::QueryFlags QmlObjectScriptClass::queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id) -{ - Q_UNUSED(flags); - QObject *obj = object.data().toQObject(); - QueryFlags rv = 0; - QString propName = name.toString(); - - if (obj) - rv = QmlEnginePrivate::get(engine)->queryObject(propName, id, obj); - - return rv; -} - -QScriptValue QmlObjectScriptClass::property(const QScriptValue &object, - const QScriptString &name, - uint id) -{ - QObject *obj = object.data().toQObject(); - - QScriptValue rv = - QmlEnginePrivate::get(engine)->propertyObject(name, obj, id); - if (rv.isValid()) - return rv; - - return QScriptValue(); -} - -void QmlObjectScriptClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value) -{ - Q_UNUSED(name); - Q_UNUSED(object); - QmlEnginePrivate::get(engine)->setPropertyObject(value, id); -} - - struct QmlEnginePrivate::ImportedNamespace { QStringList urls; QList<int> majversions; @@ -1589,6 +1122,7 @@ public: int ref; private: + friend class QmlEnginePrivate::Imports; QmlEnginePrivate::ImportedNamespace unqualifiedset; QHash<QString,QmlEnginePrivate::ImportedNamespace* > set; }; @@ -1619,6 +1153,80 @@ QmlEnginePrivate::Imports::~Imports() delete d; } +#include <QtDeclarative/qmlmetatype.h> +#include <private/qmltypenamecache_p.h> +static QmlTypeNameCache *cacheForNamespace(QmlEngine *engine, const QmlEnginePrivate::ImportedNamespace &set, QmlTypeNameCache *cache) +{ + if (!cache) + cache = new QmlTypeNameCache(engine); + + QList<QmlType *> types = QmlMetaType::qmlTypes(); + + for (int ii = 0; ii < set.urls.count(); ++ii) { + if (!set.isBuiltin.at(ii)) + continue; + + QByteArray base = set.urls.at(ii).toUtf8() + "/"; + int major = set.majversions.at(ii); + int minor = set.minversions.at(ii); + + foreach (QmlType *type, types) { + if (type->qmlTypeName().startsWith(base) && + type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && + type->majorVersion() == major && type->minMinorVersion() <= minor && + type->maxMinorVersion() >= minor) { + + QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length())); + + cache->add(name, type); + } + } + } + + return cache; +} + +QmlTypeNameCache *QmlEnginePrivate::Imports::cache(QmlEngine *engine) const +{ + const QmlEnginePrivate::ImportedNamespace &set = d->unqualifiedset; + + QmlTypeNameCache *cache = new QmlTypeNameCache(engine); + + for (QHash<QString,QmlEnginePrivate::ImportedNamespace* >::ConstIterator iter = d->set.begin(); + iter != d->set.end(); ++iter) { + + QmlTypeNameCache::Data *d = cache->data(iter.key()); + if (d) { + if (!d->typeNamespace) + cacheForNamespace(engine, *(*iter), d->typeNamespace); + } else { + QmlTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); + cache->add(iter.key(), nc); + nc->release(); + } + } + + cacheForNamespace(engine, set, cache); + + return cache; +} + +/* +QStringList QmlEnginePrivate::Imports::unqualifiedSet() const +{ + QStringList rv; + + const QmlEnginePrivate::ImportedNamespace &set = d->unqualifiedset; + + for (int ii = 0; ii < set.urls.count(); ++ii) { + if (set.isBuiltin.at(ii)) + rv << set.urls.at(ii); + } + + return rv; +} +*/ + /*! Sets the base URL to be used for all relative file imports added. */ diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 74e24d4..7978023 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -72,6 +72,10 @@ #include <QtDeclarative/qmlexpression.h> #include <QtScript/qscriptengine.h> #include <private/qmlmetaproperty_p.h> +#include <private/qmlpropertycache_p.h> +#include <private/qmlobjectscriptclass_p.h> +#include <private/qmlcontextscriptclass_p.h> +#include <private/qmlvaluetypescriptclass_p.h> QT_BEGIN_NAMESPACE @@ -88,6 +92,9 @@ class QScriptEngineDebugger; class QNetworkReply; class QNetworkAccessManager; class QmlAbstractBinding; +class QScriptDeclarativeClass; +class QmlTypeNameScriptClass; +class QmlTypeNameCache; class QmlEnginePrivate : public QObjectPrivate { @@ -98,17 +105,6 @@ public: void init(); - QScriptClass::QueryFlags queryContext(const QString &name, uint *id, - QmlContext *); - QScriptValue propertyContext(const QScriptString &propName, uint id); - void setPropertyContext(const QScriptValue &, uint id); - QScriptClass::QueryFlags queryObject(const QString &name, uint *id, - QObject *); - QScriptValue propertyObject(const QScriptString &propName, QObject *, - uint id = 0); - void setPropertyObject(const QScriptValue &, uint id); - - struct CapturedProperty { CapturedProperty(QObject *o, int c, int n) : object(o), coreIndex(c), notifyIndex(n) {} @@ -128,29 +124,12 @@ public: #endif struct ImportedNamespace; - struct ResolveData { - ResolveData() : safetyCheckId(0) {} - int safetyCheckId; - - void clear() { - object = 0; context = 0; - type = 0; ns = 0; - contextIndex = -1; isFunction = false; - } - QObject *object; - QmlContext *context; - - QmlType *type; - QmlEnginePrivate::ImportedNamespace *ns; - - int contextIndex; - bool isFunction; - QmlMetaProperty property; - } resolveData; QmlContextScriptClass *contextClass; QmlObjectScriptClass *objectClass; QmlValueTypeScriptClass *valueTypeClass; QmlTypeNameScriptClass *typeNameClass; + // Global script class + QScriptClass *globalClass; // Used by DOM Core 3 API QScriptClass *nodeListClass; QScriptClass *namedNodeMapClass; @@ -209,15 +188,21 @@ public: } QmlValueTypeFactory valueTypes; - // ### Fixme - typedef QHash<QPair<const QMetaObject *, QString>, bool> FunctionCache; - FunctionCache functionCache; - QHash<const QMetaObject *, QmlMetaObjectCache> propertyCache; - static QmlMetaObjectCache *cache(QmlEnginePrivate *priv, QObject *obj) { - if (!priv || !obj || QObjectPrivate::get(obj)->metaObject) return 0; - return &priv->propertyCache[obj->metaObject()]; + + QHash<const QMetaObject *, QmlPropertyCache *> propertyCache; + QmlPropertyCache *cache(QObject *obj) { + Q_Q(QmlEngine); + if (!obj || QObjectPrivate::get(obj)->metaObject) return 0; + const QMetaObject *mo = obj->metaObject(); + QmlPropertyCache *rv = propertyCache.value(mo); + if (!rv) { + rv = QmlPropertyCache::create(q, mo); + propertyCache.insert(mo, rv); + } + return rv; } + // ### This whole class is embarrassing struct Imports { Imports(); ~Imports(); @@ -227,6 +212,8 @@ public: void setBaseUrl(const QUrl& url); QUrl baseUrl() const; + QmlTypeNameCache *cache(QmlEngine *) const; + private: friend class QmlEnginePrivate; QmlImportsPrivate *d; @@ -251,6 +238,9 @@ public: QHash<int, int> m_qmlLists; QHash<int, QmlCompiledData *> m_compositeTypes; + QScriptValue scriptValueFromVariant(const QVariant &); + QVariant scriptValueToVariant(const QScriptValue &); + static QScriptValue qmlScriptObject(QObject*, QmlEngine*); static QScriptValue createComponent(QScriptContext*, QScriptEngine*); static QScriptValue createQmlObject(QScriptContext*, QScriptEngine*); @@ -275,19 +265,6 @@ public: class QmlScriptClass : public QScriptClass { public: - enum ClassId - { - InvalidId = -1, - - FunctionId = 0x80000000, - VariantPropertyId = 0x40000000, - PropertyId = 0x00000000, - - ClassIdMask = 0xC0000000, - - ClassIdSelectorMask = 0x3F000000, - }; - QmlScriptClass(QmlEngine *); static QVariant toVariant(QmlEngine *, const QScriptValue &); @@ -295,82 +272,6 @@ protected: QmlEngine *engine; }; -class QmlContextScriptClass : public QmlScriptClass -{ -public: - QmlContextScriptClass(QmlEngine *); - ~QmlContextScriptClass(); - - virtual QueryFlags queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, - uint id); - virtual void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value); -}; - -class QmlObjectScriptClass : public QmlScriptClass -{ -public: - QmlObjectScriptClass(QmlEngine *); - ~QmlObjectScriptClass(); - - virtual QScriptValue prototype () const; - QScriptValue prototypeObject; - - virtual QueryFlags queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, - uint id); - virtual void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value); -}; - -class QmlTypeNameScriptClass : public QmlScriptClass -{ -public: - QmlTypeNameScriptClass(QmlEngine *); - ~QmlTypeNameScriptClass(); - - virtual QueryFlags queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, - uint id); - -private: - QObject *object; - QmlType *type; - quint32 enumValue; -}; - -class QmlValueTypeScriptClass : public QmlScriptClass -{ -public: - QmlValueTypeScriptClass(QmlEngine *); - ~QmlValueTypeScriptClass(); - - virtual QueryFlags queryProperty(const QScriptValue &object, - const QScriptString &name, - QueryFlags flags, uint *id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, - uint id); - virtual void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, - const QScriptValue &value); -}; - QT_END_NAMESPACE #endif // QMLENGINE_P_H diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index db9d39f..23e1700 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -45,46 +45,85 @@ #include "qmlcontext_p.h" #include "qmlrewrite_p.h" #include "QtCore/qdebug.h" +#include "qmlcompiler_p.h" Q_DECLARE_METATYPE(QList<QObject *>); QT_BEGIN_NAMESPACE -QmlExpressionPrivate::QmlExpressionPrivate() +QmlExpressionData::QmlExpressionData() : expressionFunctionValid(false), expressionRewritten(false), me(0), trackChange(true), line(-1), guardList(0), guardListLength(0) { } +QmlExpressionData::~QmlExpressionData() +{ + if (guardList) { delete [] guardList; guardList = 0; } +} + +QmlExpressionPrivate::QmlExpressionPrivate() +: data(new QmlExpressionData) +{ + data->q = this; +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpressionData *d) +: data(d) +{ + data->q = this; +} + +QmlExpressionPrivate::~QmlExpressionPrivate() +{ + if (data) { data->q = 0; data->release(); data = 0; } +} + void QmlExpressionPrivate::init(QmlContext *ctxt, const QString &expr, QObject *me) { - expression = expr; + data->expression = expr; - QmlAbstractExpression::setContext(ctxt); - this->me = me; + data->QmlAbstractExpression::setContext(ctxt); + data->me = me; } void QmlExpressionPrivate::init(QmlContext *ctxt, void *expr, QmlRefCount *rc, QObject *me) { - quint32 *data = (quint32 *)expr; - Q_ASSERT(*data == BasicScriptEngineData || - *data == PreTransformedQtScriptData); - if (*data == BasicScriptEngineData) { - sse.load((const char *)(data + 1), rc); + quint32 *exprData = (quint32 *)expr; + Q_ASSERT(*exprData == BasicScriptEngineData || + *exprData == PreTransformedQtScriptData); + if (*exprData == BasicScriptEngineData) { + data->sse.load((const char *)(exprData + 1), rc); } else { - expression = QString::fromRawData((QChar *)(data + 2), data[1]); - expressionRewritten = true; - } + QmlCompiledData *dd = (QmlCompiledData *)rc; - QmlAbstractExpression::setContext(ctxt); - this->me = me; -} + data->expressionRewritten = true; + data->expression = QString::fromRawData((QChar *)(exprData + 3), exprData[2]); -QmlExpressionPrivate::~QmlExpressionPrivate() -{ - if (guardList) { delete [] guardList; guardList = 0; } + int progIdx = *(exprData + 1); + QmlEngine *engine = ctxt->engine(); + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + if (!dd->programs.at(progIdx)) { + dd->programs[progIdx] = new QScriptProgram(scriptEngine->compile(data->expression)); + } + + QmlContextPrivate *ctxtPriv = ctxt->d_func(); + QScriptContext *scriptContext = scriptEngine->pushCleanContext(); + scriptContext->pushScope(ctxtPriv->scriptValue); + if (me) + scriptContext->pushScope(ep->objectClass->newQObject(me)); + + data->expressionFunction = scriptEngine->evaluate(*dd->programs[progIdx]); + + data->expressionFunctionValid = true; + scriptEngine->popContext(); + } + + data->QmlAbstractExpression::setContext(ctxt); + data->me = me; } /*! @@ -151,7 +190,7 @@ QmlExpression::~QmlExpression() QmlEngine *QmlExpression::engine() const { Q_D(const QmlExpression); - return d->context()?d->context()->engine():0; + return d->data->context()?d->data->context()->engine():0; } /*! @@ -161,7 +200,7 @@ QmlEngine *QmlExpression::engine() const QmlContext *QmlExpression::context() const { Q_D(const QmlExpression); - return d->context(); + return d->data->context(); } /*! @@ -170,10 +209,10 @@ QmlContext *QmlExpression::context() const QString QmlExpression::expression() const { Q_D(const QmlExpression); - if (d->sse.isValid()) - return QLatin1String(d->sse.expression()); + if (d->data->sse.isValid()) + return QLatin1String(d->data->sse.expression()); else - return d->expression; + return d->data->expression; } /*! @@ -193,12 +232,12 @@ void QmlExpression::setExpression(const QString &expression) d->clearGuards(); - d->expression = expression; - d->expressionFunctionValid = false; - d->expressionRewritten = false; - d->expressionFunction = QScriptValue(); + d->data->expression = expression; + d->data->expressionFunctionValid = false; + d->data->expressionRewritten = false; + d->data->expressionFunction = QScriptValue(); - d->sse.clear(); + d->data->sse.clear(); } QVariant QmlExpressionPrivate::evalSSE() @@ -207,7 +246,7 @@ QVariant QmlExpressionPrivate::evalSSE() QFxPerfTimer<QFxPerf::BindValueSSE> perfsse; #endif - QVariant rv = sse.run(context(), me); + QVariant rv = data->sse.run(data->context(), data->me); return rv; } @@ -218,37 +257,39 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) QFxPerfTimer<QFxPerf::BindValueQt> perfqt; #endif - QmlContextPrivate *ctxtPriv = context()->d_func(); - QmlEngine *engine = context()->engine(); + QmlContextPrivate *ctxtPriv = data->context()->d_func(); + QmlEngine *engine = data->context()->engine(); + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); - if (me) - ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount, me); if (secondaryScope) ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount, secondaryScope); QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); - if (!expressionFunctionValid) { + if (!data->expressionFunctionValid) { + + QScriptContext *scriptContext = scriptEngine->pushCleanContext(); + scriptContext->pushScope(ctxtPriv->scriptValue); - QScriptContext *scriptContext = scriptEngine->pushContext(); - for (int i = ctxtPriv->scopeChain.size() - 1; i > -1; --i) - scriptContext->pushScope(ctxtPriv->scopeChain.at(i)); + if (data->me) + scriptContext->pushScope(ep->objectClass->newQObject(data->me)); - if (expressionRewritten) { - expressionFunction = scriptEngine->evaluate(expression, fileName, line); + if (data->expressionRewritten) { + data->expressionFunction = scriptEngine->evaluate(data->expression, + data->fileName, data->line); } else { QmlRewrite::RewriteBinding rewriteBinding; - const QString code = rewriteBinding(expression); - expressionFunction = scriptEngine->evaluate(code, fileName, line); + const QString code = rewriteBinding(data->expression); + data->expressionFunction = scriptEngine->evaluate(code, data->fileName, data->line); } scriptEngine->popContext(); - expressionFunctionValid = true; + data->expressionFunctionValid = true; } - QScriptValue svalue = expressionFunction.call(); + QScriptValue svalue = data->expressionFunction.call(); if (scriptEngine->hasUncaughtException()) { if (scriptEngine->uncaughtException().isError()){ @@ -264,8 +305,6 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) } } - if (me) - ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); if (secondaryScope) ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); @@ -277,13 +316,8 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) QList<QObject *> list; for (int ii = 0; ii < length; ++ii) { QScriptValue arrayItem = svalue.property(ii); - QObject *d = - qvariant_cast<QObject *>(arrayItem.data().toVariant()); - if (d) { - list << d; - } else { - list << 0; - } + QObject *d = arrayItem.toQObject(); + list << d; } rv = QVariant::fromValue(list); } @@ -297,13 +331,10 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope) !svalue.isQMetaObject() && !svalue.isQObject() && !svalue.isRegExp()) { - QScriptValue objValue = svalue.data(); - if (objValue.isValid()) { - QVariant var = objValue.toVariant(); - if (var.userType() >= (int)QVariant::UserType && - QmlMetaType::isObject(var.userType())) - rv = var; - } + + QObject *o = svalue.toQObject(); + if (o) + return qVariantFromValue(o); } if (rv.isNull()) rv = svalue.toVariant(); @@ -316,7 +347,7 @@ QVariant QmlExpressionPrivate::value(QObject *secondaryScope) Q_Q(QmlExpression); QVariant rv; - if (!q->engine() || (!sse.isValid() && expression.isEmpty())) + if (!q->engine() || (!data->sse.isValid() && data->expression.isEmpty())) return rv; #ifdef Q_ENABLE_PERFORMANCE_LOG @@ -331,7 +362,11 @@ QVariant QmlExpressionPrivate::value(QObject *secondaryScope) ep->currentExpression = q; - if (sse.isValid()) { + // This object might be deleted during the eval + QmlExpressionData *localData = data; + localData->addref(); + + if (data->sse.isValid()) { rv = evalSSE(); } else { rv = evalQtScript(secondaryScope); @@ -339,12 +374,17 @@ QVariant QmlExpressionPrivate::value(QObject *secondaryScope) ep->currentExpression = lastCurrentExpression; - if ((!q->trackChange() || !ep->capturedProperties.count()) && guardList) { - clearGuards(); - } else if(q->trackChange()) { - updateGuards(ep->capturedProperties); + // Check if we were deleted + if (localData->q) { + if ((!data->trackChange || !ep->capturedProperties.count()) && data->guardList) { + clearGuards(); + } else if(data->trackChange) { + updateGuards(ep->capturedProperties); + } } + localData->release(); + lastCapturedProperties.copyAndClear(ep->capturedProperties); return rv; @@ -368,7 +408,7 @@ QVariant QmlExpression::value() bool QmlExpression::isConstant() const { Q_D(const QmlExpression); - return !d->guardList; + return !d->data->guardList; } /*! @@ -377,7 +417,7 @@ bool QmlExpression::isConstant() const bool QmlExpression::trackChange() const { Q_D(const QmlExpression); - return d->trackChange; + return d->data->trackChange; } /*! @@ -398,7 +438,7 @@ bool QmlExpression::trackChange() const void QmlExpression::setTrackChange(bool trackChange) { Q_D(QmlExpression); - d->trackChange = trackChange; + d->data->trackChange = trackChange; } /*! @@ -408,8 +448,8 @@ void QmlExpression::setTrackChange(bool trackChange) void QmlExpression::setSourceLocation(const QUrl &fileName, int line) { Q_D(QmlExpression); - d->fileName = fileName.toString(); - d->line = line; + d->data->fileName = fileName.toString(); + d->data->line = line; } /*! @@ -421,7 +461,7 @@ void QmlExpression::setSourceLocation(const QUrl &fileName, int line) QObject *QmlExpression::scopeObject() const { Q_D(const QmlExpression); - return d->me; + return d->data->me; } /*! \internal */ @@ -439,16 +479,16 @@ void QmlExpressionPrivate::clearGuards() notifyIdx = QmlExpression::staticMetaObject.indexOfMethod("__q_notify()"); - for (int ii = 0; ii < guardListLength; ++ii) { - if (guardList[ii].data()) { - QMetaObject::disconnect(guardList[ii].data(), - guardList[ii].notifyIndex, + for (int ii = 0; ii < data->guardListLength; ++ii) { + if (data->guardList[ii].data()) { + QMetaObject::disconnect(data->guardList[ii].data(), + data->guardList[ii].notifyIndex, q, notifyIdx); } } - delete [] guardList; guardList = 0; - guardListLength = 0; + delete [] data->guardList; data->guardList = 0; + data->guardListLength = 0; } void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::CapturedProperty> &properties) @@ -461,10 +501,10 @@ void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::Captu notifyIdx = QmlExpression::staticMetaObject.indexOfMethod("__q_notify()"); - SignalGuard *newGuardList = 0; + QmlExpressionData::SignalGuard *newGuardList = 0; - if (properties.count() != guardListLength) - newGuardList = new SignalGuard[properties.count()]; + if (properties.count() != data->guardListLength) + newGuardList = new QmlExpressionData::SignalGuard[properties.count()]; bool outputWarningHeader = false; int hit = 0; @@ -472,20 +512,20 @@ void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::Captu const QmlEnginePrivate::CapturedProperty &property = properties.at(ii); bool needGuard = true; - if (ii >= guardListLength) { + if (ii >= data->guardListLength) { // New guard - } else if(guardList[ii].data() == property.object && - guardList[ii].notifyIndex == property.notifyIndex) { + } else if(data->guardList[ii].data() == property.object && + data->guardList[ii].notifyIndex == property.notifyIndex) { // Cache hit - if (!guardList[ii].isDuplicate || - (guardList[ii].isDuplicate && hit == ii)) { + if (!data->guardList[ii].isDuplicate || + (data->guardList[ii].isDuplicate && hit == ii)) { needGuard = false; ++hit; } - } else if(guardList[ii].data() && !guardList[ii].isDuplicate) { + } else if(data->guardList[ii].data() && !data->guardList[ii].isDuplicate) { // Cache miss - QMetaObject::disconnect(guardList[ii].data(), - guardList[ii].notifyIndex, + QMetaObject::disconnect(data->guardList[ii].data(), + data->guardList[ii].notifyIndex, q, notifyIdx); } /* else { @@ -494,9 +534,9 @@ void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::Captu if (needGuard) { if (!newGuardList) { - newGuardList = new SignalGuard[properties.count()]; + newGuardList = new QmlExpressionData::SignalGuard[properties.count()]; for (int jj = 0; jj < ii; ++jj) - newGuardList[jj] = guardList[jj]; + newGuardList[jj] = data->guardList[jj]; } if (property.notifyIndex != -1) { @@ -526,22 +566,22 @@ void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::Captu << "::" << metaProp.name(); } } else if (newGuardList) { - newGuardList[ii] = guardList[ii]; + newGuardList[ii] = data->guardList[ii]; } } - for (int ii = properties.count(); ii < guardListLength; ++ii) { - if (guardList[ii].data() && !guardList[ii].isDuplicate) { - QMetaObject::disconnect(guardList[ii].data(), - guardList[ii].notifyIndex, + for (int ii = properties.count(); ii < data->guardListLength; ++ii) { + if (data->guardList[ii].data() && !data->guardList[ii].isDuplicate) { + QMetaObject::disconnect(data->guardList[ii].data(), + data->guardList[ii].notifyIndex, q, notifyIdx); } } if (newGuardList) { - if (guardList) delete [] guardList; - guardList = newGuardList; - guardListLength = properties.count(); + if (data->guardList) delete [] data->guardList; + data->guardList = newGuardList; + data->guardListLength = properties.count(); } } diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h index 501e5d8..33016e6 100644 --- a/src/declarative/qml/qmlexpression_p.h +++ b/src/declarative/qml/qmlexpression_p.h @@ -79,23 +79,13 @@ private: QmlAbstractExpression *m_nextExpression; }; -class QmlExpression; -class QString; -class QmlExpressionPrivate : public QObjectPrivate, public QmlAbstractExpression +class QmlExpressionData : public QmlAbstractExpression, public QmlRefCount { - Q_DECLARE_PUBLIC(QmlExpression) public: - QmlExpressionPrivate(); - ~QmlExpressionPrivate(); - - enum CompiledDataType { - BasicScriptEngineData = 1, - PreTransformedQtScriptData = 2 - }; + QmlExpressionData(); + ~QmlExpressionData(); - - void init(QmlContext *, const QString &, QObject *); - void init(QmlContext *, void *, QmlRefCount *, QObject *); + QmlExpressionPrivate *q; QString expression; bool expressionFunctionValid:1; @@ -109,10 +99,6 @@ public: QString fileName; int line; - QVariant value(QObject *secondaryScope = 0); - QVariant evalSSE(); - QVariant evalQtScript(QObject *secondaryScope); - struct SignalGuard : public QGuard<QObject> { SignalGuard() : isDuplicate(false), notifyIndex(-1) {} @@ -132,6 +118,32 @@ public: }; SignalGuard *guardList; int guardListLength; +}; + +class QmlExpression; +class QString; +class QmlExpressionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlExpression) +public: + QmlExpressionPrivate(); + QmlExpressionPrivate(QmlExpressionData *); + ~QmlExpressionPrivate(); + + enum CompiledDataType { + BasicScriptEngineData = 1, + PreTransformedQtScriptData = 2 + }; + + void init(QmlContext *, const QString &, QObject *); + void init(QmlContext *, void *, QmlRefCount *, QObject *); + + QmlExpressionData *data; + + QVariant value(QObject *secondaryScope = 0); + QVariant evalSSE(); + QVariant evalQtScript(QObject *secondaryScope); + void updateGuards(const QPODVector<QmlEnginePrivate::CapturedProperty> &properties); void clearGuards(); diff --git a/src/declarative/qml/qmlglobalscriptclass.cpp b/src/declarative/qml/qmlglobalscriptclass.cpp new file mode 100644 index 0000000..0ade5ee --- /dev/null +++ b/src/declarative/qml/qmlglobalscriptclass.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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 "qmlglobalscriptclass_p.h" +#include <QtScript/qscriptstring.h> +#include <QtScript/qscriptengine.h> +#include <QtScript/qscriptvalueiterator.h> + +QT_BEGIN_NAMESPACE + +/* + Used to prevent any writes to the global object. +*/ +QmlGlobalScriptClass::QmlGlobalScriptClass(QScriptEngine *engine) +: QScriptClass(engine) +{ + QScriptValue v = engine->newObject(); + globalObject = engine->globalObject(); + + QScriptValueIterator iter(globalObject); + while (iter.hasNext()) { + iter.next(); + v.setProperty(iter.scriptName(), iter.value()); + } + + v.setScriptClass(this); + engine->setGlobalObject(v); +} + +QScriptClass::QueryFlags +QmlGlobalScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + return HandlesReadAccess | HandlesWriteAccess; +} + +QScriptValue +QmlGlobalScriptClass::property(const QScriptValue &object, + const QScriptString &name, + uint id) +{ + return engine()->undefinedValue(); +} + +void QmlGlobalScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value) +{ + QString error = QLatin1String("Invalid write to global property \"") + + name.toString() + QLatin1String("\""); + engine()->currentContext()->throwError(error); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/util/qmlscript.h b/src/declarative/qml/qmlglobalscriptclass_p.h index 4ba4f6b..1b58f1e 100644 --- a/src/declarative/util/qmlscript.h +++ b/src/declarative/qml/qmlglobalscriptclass_p.h @@ -39,46 +39,43 @@ ** ****************************************************************************/ -#ifndef QMLSCRIPT_H -#define QMLSCRIPT_H +#ifndef QMLGLOBALSCRIPTCLASS_P_H +#define QMLGLOBALSCRIPTCLASS_P_H -#include <QtDeclarative/qfxglobal.h> -#include <QtCore/qobject.h> -#include <QtDeclarative/qml.h> +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// -QT_BEGIN_HEADER +#include <QtScript/qscriptclass.h> QT_BEGIN_NAMESPACE -QT_MODULE(Declarative) - -class QmlScriptPrivate; -class Q_DECLARATIVE_EXPORT QmlScript : public QObject +class QmlGlobalScriptClass : public QScriptClass { - Q_OBJECT - Q_DECLARE_PRIVATE(QmlScript) - - Q_PROPERTY(QString script READ script WRITE setScript) - Q_PROPERTY(QUrl source READ source WRITE setSource) - Q_CLASSINFO("DefaultProperty", "script") - public: - QmlScript(QObject *parent=0); + QmlGlobalScriptClass(QScriptEngine *); - QString script() const; - void setScript(const QString &); + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); - QUrl source() const; - void setSource(const QUrl &); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); -private Q_SLOTS: - void replyFinished(); + virtual void setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value); + +private: + QScriptValue globalObject; }; QT_END_NAMESPACE -QML_DECLARE_TYPE(QmlScript) - -QT_END_HEADER - -#endif +#endif // QMLGLOBALSCRIPTCLASS_P_H diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index b71c6e3..18439f4 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -69,7 +69,7 @@ void QmlCompiledData::dump(QmlInstruction *instr, int idx) qWarning() << idx << "\t" << line << "\t" << "CREATE_COMPONENT\t" << instr->createComponent.count; break; case QmlInstruction::StoreMetaObject: - qWarning() << idx << "\t" << line << "\t" << "STORE_META\t\t" << instr->storeMeta.data << "\t" << instr->storeMeta.slotData; + qWarning() << idx << "\t" << line << "\t" << "STORE_META\t\t" << instr->storeMeta.data << "\t"; break; case QmlInstruction::StoreFloat: qWarning() << idx << "\t" << line << "\t" << "STORE_FLOAT\t\t" << instr->storeFloat.propertyIndex << "\t" << instr->storeFloat.value; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 2c9ceac..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 @@ -165,7 +166,7 @@ public: struct { int bindingsSize; int parserStatusSize; - int idSize; + int contextCache; } init; struct { int type; @@ -175,8 +176,8 @@ public: } create; struct { int data; - int slotData; int aliasData; + int propertyCache; } storeMeta; struct { int value; @@ -238,6 +239,9 @@ public: int value; } storeString; struct { + int value; + } storeScript; + struct { int propertyIndex; int value; } storeUrl; diff --git a/src/declarative/qml/qmlintegercache.cpp b/src/declarative/qml/qmlintegercache.cpp new file mode 100644 index 0000000..564faaa --- /dev/null +++ b/src/declarative/qml/qmlintegercache.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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 "qmlintegercache_p.h" +#include <private/qmlengine_p.h> +#include <QtDeclarative/qmlmetatype.h> + +QT_BEGIN_NAMESPACE + +QmlIntegerCache::QmlIntegerCache(QmlEngine *e) +: engine(e) +{ +} + +QmlIntegerCache::~QmlIntegerCache() +{ + qDeleteAll(stringCache); +} + +void QmlIntegerCache::add(const QString &id, int value) +{ + Q_ASSERT(!stringCache.contains(id)); + + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + + // ### use contextClass + Data *d = new Data(enginePriv->objectClass->createPersistentIdentifier(id), value); + + stringCache.insert(id, d); + identifierCache.insert(d->identifier, d); +} + +int QmlIntegerCache::value(const QString &id) +{ + Data *d = stringCache.value(id); + return d?d->value:-1; +} + +QmlIntegerCache *QmlIntegerCache::createForEnums(QmlType *type, QmlEngine *engine) +{ + Q_ASSERT(type); + Q_ASSERT(engine); + + QmlIntegerCache *cache = new QmlIntegerCache(engine); + + const QMetaObject *mo = type->metaObject(); + + for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum enumerator = mo->enumerator(ii); + + for (int jj = 0; jj < enumerator.keyCount(); ++jj) { + QString name = QLatin1String(enumerator.key(jj)); + int value = enumerator.value(jj); + + if (!name.at(0).isUpper()) + continue; + + if (cache->stringCache.contains(name)) + continue; + + cache->add(name, value); + } + } + + return cache; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlintegercache_p.h b/src/declarative/qml/qmlintegercache_p.h new file mode 100644 index 0000000..e11e0be --- /dev/null +++ b/src/declarative/qml/qmlintegercache_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLINTEGERCACHE_P_H +#define QMLINTEGERCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qmlrefcount_p.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <QtCore/qhash.h> + +QT_BEGIN_NAMESPACE + +class QmlType; +class QmlEngine; +class QmlIntegerCache : public QmlRefCount +{ +public: + QmlIntegerCache(QmlEngine *); + virtual ~QmlIntegerCache(); + + inline int count() const; + void add(const QString &, int); + int value(const QString &); + inline int value(const QScriptDeclarativeClass::Identifier &id) const; + + static QmlIntegerCache *createForEnums(QmlType *, QmlEngine *); +private: + struct Data : public QScriptDeclarativeClass::PersistentIdentifier { + Data(const QScriptDeclarativeClass::PersistentIdentifier &i, int v) + : QScriptDeclarativeClass::PersistentIdentifier(i), value(v) {} + + int value; + }; + + typedef QHash<QString, Data *> StringCache; + typedef QHash<QScriptDeclarativeClass::Identifier, Data *> IdentifierCache; + + StringCache stringCache; + IdentifierCache identifierCache; + QmlEngine *engine; +}; + +int QmlIntegerCache::value(const QScriptDeclarativeClass::Identifier &id) const +{ + Data *d = identifierCache.value(id); + return d?d->value:-1; +} + +int QmlIntegerCache::count() const +{ + return stringCache.count(); +} + +QT_END_NAMESPACE + +#endif // QMLINTEGERCACHE_P_H + diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index efc4a2b..302ce8c 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -58,56 +58,6 @@ Q_DECLARE_METATYPE(QList<QObject *>); QT_BEGIN_NAMESPACE -QmlMetaObjectCache::QmlMetaObjectCache() -: propertyCache(0) -{ -} - -void QmlMetaObjectCache::init(const QMetaObject *metaObject) -{ - if (propertyCache || !metaObject) - return; - - int propCount = metaObject->propertyCount(); - - propertyCache = new Data[propCount]; - for (int ii = 0; ii < propCount; ++ii) { - QMetaProperty p = metaObject->property(ii); - propertyCache[ii].propType = p.userType(); - propertyCache[ii].coreIndex = ii; - propertyCache[ii].name = QLatin1String(p.name()); - - propertyNameCache.insert(propertyCache[ii].name, ii); - } -} - -QmlMetaObjectCache::~QmlMetaObjectCache() -{ - delete [] propertyCache; -} - -QmlMetaObjectCache::Data * -QmlMetaObjectCache::property(int index, const QMetaObject *metaObject) -{ - init(metaObject); - - return propertyCache + index; -} - -QmlMetaObjectCache::Data * -QmlMetaObjectCache::property(const QString &name, const QMetaObject *metaObject) -{ - init(metaObject); - - QHash<QString, int>::ConstIterator iter = propertyNameCache.find(name); - - if (iter != propertyNameCache.end()) { - return propertyCache + *iter; - } else { - return 0; - } -} - /*! \class QmlMetaProperty \brief The QmlMetaProperty class abstracts accessing QML properties. @@ -122,9 +72,7 @@ QmlMetaProperty::QmlMetaProperty() d->q = this; } -/*! - The destructor deletes its heap data. - */ +/*! \internal */ QmlMetaProperty::~QmlMetaProperty() { delete d; d = 0; @@ -164,11 +112,9 @@ void QmlMetaPropertyPrivate::initDefault(QObject *obj) object = obj; QMetaProperty p = QmlMetaType::defaultProperty(obj); - name = QLatin1String(p.name()); - propType = p.userType();; - coreIdx = p.propertyIndex(); - if (!name.isEmpty()) - type = QmlMetaProperty::Property | QmlMetaProperty::Default; + core.load(p); + if (core.isValid()) + isDefaultProperty = true; } /*! @@ -179,15 +125,12 @@ void QmlMetaPropertyPrivate::initDefault(QObject *obj) QmlMetaProperty::QmlMetaProperty(QObject *obj, int idx, QmlContext *ctxt) : d(new QmlMetaPropertyPrivate) { + Q_ASSERT(obj); + d->q = this; d->context = ctxt; d->object = obj; - d->type = Property; - QMetaProperty p = obj->metaObject()->property(idx); - d->propType = p.userType(); - d->coreIdx = idx; - if (p.name() != 0) - d->name = QLatin1String(p.name()); + d->core.load(obj->metaObject()->property(idx)); } /*! @@ -218,7 +161,6 @@ void QmlMetaPropertyPrivate::initProperty(QObject *obj, const QString &name) if (context && context->engine()) enginePrivate = QmlEnginePrivate::get(context->engine()); - this->name = name; object = obj; if (name.isEmpty() || !obj) @@ -235,8 +177,6 @@ void QmlMetaPropertyPrivate::initProperty(QObject *obj, const QString &name) enginePrivate->resolveType(typeData->imports, name.toLatin1(), &t, 0, 0, 0, 0); if (t && t->attachedPropertiesFunction()) { attachedFunc = t->index(); - if (attachedFunc != -1) - type = QmlMetaProperty::Property | QmlMetaProperty::Attached; } typeData->release(); } @@ -250,30 +190,31 @@ void QmlMetaPropertyPrivate::initProperty(QObject *obj, const QString &name) QString signalName = name.mid(2); signalName[0] = signalName.at(0).toLower(); - findSignalInt(obj, signalName); - if (signal.signature() != 0) { - type = QmlMetaProperty::SignalProperty; + QMetaMethod method = findSignal(obj, signalName); + if (method.signature()) { + core.load(method); return; } } // Property - QmlMetaObjectCache *cache = QmlEnginePrivate::cache(enginePrivate, obj); + QmlPropertyCache *cache = 0; + QmlDeclarativeData *ddata = QmlDeclarativeData::get(obj); + if (ddata) + cache = ddata->propertyCache; + if (!cache) + cache = enginePrivate?enginePrivate->cache(obj):0; + if (cache) { - QmlMetaObjectCache::Data *data = - cache->property(name, obj->metaObject()); - if (data) { - type = QmlMetaProperty::Property; - propType = data->propType; - coreIdx = data->coreIndex; - } + QmlPropertyCache::Data *data = cache->property(name); + + if (data && !(data->flags & QmlPropertyCache::Data::IsFunction)) + core = *data; + } else { - // Can't cache + // No cache available QMetaProperty p = QmlMetaType::property(obj, name.toUtf8().constData()); - propType = p.userType(); - coreIdx = p.propertyIndex(); - if (p.name()) - type = QmlMetaProperty::Property; + core.load(p); } } @@ -323,33 +264,31 @@ QmlMetaProperty::PropertyCategory QmlMetaProperty::propertyCategory() const QmlMetaProperty::PropertyCategory QmlMetaPropertyPrivate::propertyCategory() const { - if (category == QmlMetaProperty::Unknown) { + uint type = q->type(); + + if (type & QmlMetaProperty::ValueTypeProperty) { + return QmlMetaProperty::Normal; + } else if (type & QmlMetaProperty::Attached) { + return QmlMetaProperty::Object; + } else if (type & QmlMetaProperty::Property) { int type = propertyType(); - if (type == QmlMetaProperty::Invalid) - category = QmlMetaProperty::InvalidProperty; - else if (type < QVariant::UserType) - category = QmlMetaProperty::Normal; + if (type == QVariant::Invalid) + return QmlMetaProperty::InvalidProperty; + else if ((uint)type < QVariant::UserType) + return QmlMetaProperty::Normal; else if (type == qMetaTypeId<QmlBinding *>()) - category = QmlMetaProperty::Bindable; - else { - QmlMetaType::TypeCategory tc = QmlMetaType::typeCategory(type); - switch(tc) { - case QmlMetaType::Object: - category = QmlMetaProperty::Object; - break; - case QmlMetaType::QmlList: - category = QmlMetaProperty::QmlList; - break; - case QmlMetaType::List: - category = QmlMetaProperty::List; - break; - case QmlMetaType::Unknown: - category = QmlMetaProperty::Normal; - break; - } - } + return QmlMetaProperty::Bindable; + else if (core.flags & QmlPropertyCache::Data::IsQObjectDerived) + return QmlMetaProperty::Object; + else if (core.flags & QmlPropertyCache::Data::IsQmlList) + return QmlMetaProperty::QmlList; + else if (core.flags & QmlPropertyCache::Data::IsQList) + return QmlMetaProperty::List; + else + return QmlMetaProperty::Normal; + } else { + return QmlMetaProperty::InvalidProperty; } - return category; } /*! @@ -358,8 +297,21 @@ QmlMetaPropertyPrivate::propertyCategory() const */ const char *QmlMetaProperty::propertyTypeName() const { - if (!d->name.isEmpty() && d->object) { - return d->object->metaObject()->property(d->coreIdx).typeName(); + if (type() & ValueTypeProperty) { + + QmlEnginePrivate *ep = d->context?QmlEnginePrivate::get(d->context->engine()):0; + QmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QmlValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + const char *rv = valueType->metaObject()->property(d->valueTypeCoreIdx).typeName(); + + if (!ep) delete valueType; + + return rv; + } else if (d->object && type() & Property && d->core.isValid()) { + return d->object->metaObject()->property(d->core.coreIndex).typeName(); } else { return 0; } @@ -371,10 +323,13 @@ const char *QmlMetaProperty::propertyTypeName() const */ bool QmlMetaProperty::operator==(const QmlMetaProperty &other) const { - return d->name == other.d->name && - d->signal.signature() == other.d->signal.signature() && - d->type == other.d->type && - d->object == other.d->object; + // category is intentially omitted here as it is generated + // from the other members + return d->object == other.d->object && + d->core == other.d->core && + d->valueTypeCoreIdx == other.d->valueTypeCoreIdx && + d->valueTypePropType == other.d->valueTypePropType && + d->attachedFunc == other.d->attachedFunc; } /*! @@ -388,18 +343,19 @@ int QmlMetaProperty::propertyType() const int QmlMetaPropertyPrivate::propertyType() const { - int rv = QVariant::Invalid; - - if (!name.isEmpty()) { - if (propType == (int)QVariant::LastType) - rv = qMetaTypeId<QVariant>(); + uint type = q->type(); + if (type & QmlMetaProperty::ValueTypeProperty) { + return valueTypePropType; + } else if (type & QmlMetaProperty::Attached) { + return qMetaTypeId<QObject *>(); + } else if (type & QmlMetaProperty::Property) { + if (core.propType == (int)QVariant::LastType) + return qMetaTypeId<QVariant>(); else - rv = propType; - } else if (attachedFunc) { - rv = qMetaTypeId<QObject *>(); - } - - return rv; + return core.propType; + } else { + return QVariant::Invalid; + } } /*! @@ -407,7 +363,16 @@ int QmlMetaPropertyPrivate::propertyType() const */ QmlMetaProperty::Type QmlMetaProperty::type() const { - return (Type)d->type; + if (d->core.flags & QmlPropertyCache::Data::IsFunction) + return SignalProperty; + else if (d->attachedFunc != -1) + return Attached; + else if (d->valueTypeCoreIdx != -1) + return (Type)(Property | ValueTypeProperty); + else if (d->core.isValid()) + return (Type)(Property | ((d->isDefaultProperty)?Default:0)); + else + return Invalid; } /*! @@ -439,17 +404,16 @@ QObject *QmlMetaProperty::object() const */ QmlMetaProperty &QmlMetaProperty::operator=(const QmlMetaProperty &other) { - d->name = other.d->name; - d->signal = other.d->signal; d->context = other.d->context; - d->coreIdx = other.d->coreIdx; - d->valueTypeIdx = other.d->valueTypeIdx; - d->valueTypeId = other.d->valueTypeId; - d->type = other.d->type; - d->attachedFunc = other.d->attachedFunc; d->object = other.d->object; - d->propType = other.d->propType; - d->category = other.d->category; + + d->isDefaultProperty = other.d->isDefaultProperty; + d->core = other.d->core; + + d->valueTypeCoreIdx = other.d->valueTypeCoreIdx; + d->valueTypePropType = other.d->valueTypePropType; + + d->attachedFunc = other.d->attachedFunc; return *this; } @@ -458,12 +422,14 @@ QmlMetaProperty &QmlMetaProperty::operator=(const QmlMetaProperty &other) */ bool QmlMetaProperty::isWritable() const { - if (propertyCategory() == List || propertyCategory() == QmlList) + QmlMetaProperty::PropertyCategory category = propertyCategory(); + + if (category == List || category == QmlList) return true; else if (type() & SignalProperty) return true; - else if (!d->name.isEmpty() && d->object) - return d->object->metaObject()->property(d->coreIdx).isWritable(); + else if (d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex).isWritable(); else return false; } @@ -473,8 +439,8 @@ bool QmlMetaProperty::isWritable() const */ bool QmlMetaProperty::isDesignable() const { - if (!d->name.isEmpty() && d->object) - return d->object->metaObject()->property(d->coreIdx).isDesignable(); + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); else return false; } @@ -493,6 +459,7 @@ bool QmlMetaProperty::isValid() const */ QStringList QmlMetaProperty::properties(QObject *obj) { + // ### What is this used for? if (!obj) return QStringList(); @@ -511,7 +478,23 @@ QStringList QmlMetaProperty::properties(QObject *obj) */ QString QmlMetaProperty::name() const { - return d->name; + if (type() & ValueTypeProperty) { + QString rv = d->core.name + QLatin1String("."); + + QmlEnginePrivate *ep = d->context?QmlEnginePrivate::get(d->context->engine()):0; + QmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[d->core.propType]; + else valueType = QmlValueTypeFactory::valueType(d->core.propType); + Q_ASSERT(valueType); + + rv += QLatin1String(valueType->metaObject()->property(d->valueTypeCoreIdx).name()); + + if (!ep) delete valueType; + + return rv; + } else { + return d->core.name; + } } /*! @@ -520,13 +503,26 @@ QString QmlMetaProperty::name() const */ QMetaProperty QmlMetaProperty::property() const { - if (d->object) - return d->object->metaObject()->property(d->coreIdx); + if (type() & Property && d->core.isValid() && d->object) + return d->object->metaObject()->property(d->core.coreIndex); else return QMetaProperty(); } /*! + Return the QMetaMethod for this property if it is a SignalProperty, + otherwise returns an invalid QMetaMethod. +*/ +QMetaMethod QmlMetaProperty::method() const +{ + if (type() & SignalProperty && d->object) + return d->object->metaObject()->method(d->core.coreIndex); + else + return QMetaMethod(); +} + + +/*! Returns the binding associated with this property, or 0 if no binding exists. */ @@ -539,13 +535,13 @@ QmlAbstractBinding *QmlMetaProperty::binding() const if (!data) return 0; - if (!data->hasBindingBit(d->coreIdx)) + if (!data->hasBindingBit(d->core.coreIndex)) return 0; QmlAbstractBinding *binding = data->bindings; while (binding) { // ### This wont work for value types - if (binding->propertyIndex() == d->coreIdx) + if (binding->propertyIndex() == d->core.coreIndex) return binding; binding = binding->m_nextBinding; } @@ -568,14 +564,20 @@ QmlMetaProperty::setBinding(QmlAbstractBinding *newBinding) const if (!isProperty() || (type() & Attached) || !d->object) return 0; - QmlDeclarativeData *data = - QmlDeclarativeData::get(d->object, 0 != newBinding); + return d->setBinding(d->object, d->core, newBinding); +} + +QmlAbstractBinding * +QmlMetaPropertyPrivate::setBinding(QObject *object, const QmlPropertyCache::Data &core, + QmlAbstractBinding *newBinding) +{ + QmlDeclarativeData *data = QmlDeclarativeData::get(object, 0 != newBinding); - if (data && data->hasBindingBit(d->coreIdx)) { + if (data && data->hasBindingBit(core.coreIndex)) { QmlAbstractBinding *binding = data->bindings; while (binding) { // ### This wont work for value types - if (binding->propertyIndex() == d->coreIdx) { + if (binding->propertyIndex() == core.coreIndex) { binding->setEnabled(false); if (newBinding) @@ -638,15 +640,14 @@ QmlExpression *QmlMetaProperty::setSignalExpression(QmlExpression *expr) const } if (expr) { - QmlBoundSignal *signal = new QmlBoundSignal(d->object, d->signal, - d->object); + QmlBoundSignal *signal = new QmlBoundSignal(d->object, method(), d->object); return signal->setExpression(expr); } else { return 0; } } -void QmlMetaPropertyPrivate::findSignalInt(QObject *obj, const QString &name) +QMetaMethod QmlMetaPropertyPrivate::findSignal(QObject *obj, const QString &name) { const QMetaObject *mo = obj->metaObject(); @@ -657,12 +658,10 @@ void QmlMetaPropertyPrivate::findSignalInt(QObject *obj, const QString &name) int idx = methodName.indexOf(QLatin1Char('(')); methodName = methodName.left(idx); - if (methodName == name) { - signal = method; - coreIdx = ii; - return; - } + if (methodName == name) + return method; } + return QMetaMethod(); } QObject *QmlMetaPropertyPrivate::attachedObject() const @@ -687,31 +686,14 @@ QVariant QmlMetaProperty::read() const for (int ii = 0; ii < children.count(); ++ii) { QmlBoundSignal *sig = QmlBoundSignal::cast(children.at(ii)); - if (sig && sig->index() == d->coreIdx) + if (sig && sig->index() == d->core.coreIndex) return sig->expression()->expression(); } + } else if (type() & Property) { - if (type() & Attached) { - return QVariant::fromValue(d->attachedObject()); - } else if(type() & ValueTypeProperty) { - QmlEnginePrivate *ep = d->context?static_cast<QmlEnginePrivate *>(QObjectPrivate::get(d->context->engine())):0; - QmlValueType *valueType = 0; - if (ep) - valueType = ep->valueTypes[d->valueTypeId]; - else - valueType = QmlValueTypeFactory::valueType(d->valueTypeId); - Q_ASSERT(valueType); - - valueType->read(object(), d->coreIdx); - QVariant rv = - valueType->metaObject()->property(d->valueTypeIdx).read(valueType); - if (!ep) - delete valueType; - return rv; - } else { - return d->object->metaObject()->property(d->coreIdx).read(object()); - } + return d->readValueProperty(); + } return QVariant(); } @@ -723,7 +705,7 @@ void QmlMetaPropertyPrivate::writeSignalProperty(const QVariant &value) for (int ii = 0; ii < children.count(); ++ii) { QmlBoundSignal *sig = QmlBoundSignal::cast(children.at(ii)); - if (sig && sig->index() == coreIdx) { + if (sig && sig->index() == core.coreIndex) { if (expr.isEmpty()) { sig->disconnect(); sig->deleteLater(); @@ -736,47 +718,86 @@ void QmlMetaPropertyPrivate::writeSignalProperty(const QVariant &value) if (!expr.isEmpty()) { // XXX scope - (void *)new QmlBoundSignal(qmlContext(object), expr, object, signal, object); + (void *)new QmlBoundSignal(qmlContext(object), expr, object, q->method(), object); + } +} + +QVariant QmlMetaPropertyPrivate::readValueProperty() +{ + uint type = q->type(); + if (type & QmlMetaProperty::Attached) { + + return QVariant::fromValue(attachedObject()); + + } else if(type & QmlMetaProperty::ValueTypeProperty) { + + QmlEnginePrivate *ep = context?QmlEnginePrivate::get(context->engine()):0; + QmlValueType *valueType = 0; + if (ep) valueType = ep->valueTypes[core.propType]; + else valueType = QmlValueTypeFactory::valueType(core.propType); + Q_ASSERT(valueType); + + valueType->read(object, core.coreIndex); + + QVariant rv = + valueType->metaObject()->property(valueTypeCoreIdx).read(valueType); + + if (!ep) delete valueType; + return rv; + + } else { + + return object->metaObject()->property(core.coreIndex).read(object.data()); + } } void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, QmlMetaProperty::WriteSource source) { - QObject *object = this->object; - int coreIdx = this->coreIdx; - - QmlValueType *writeBack = 0; - QObject *writeBackObj = 0; - int writeBackIdx = -1; - bool deleteWriteBack = false; - // Remove any existing bindings on this property if (source != QmlMetaProperty::Binding) delete q->setBinding(0); + uint type = q->type(); if (type & QmlMetaProperty::ValueTypeProperty) { - QmlEnginePrivate *ep = context?static_cast<QmlEnginePrivate *>(QObjectPrivate::get(context->engine())):0; + QmlEnginePrivate *ep = + context?static_cast<QmlEnginePrivate *>(QObjectPrivate::get(context->engine())):0; + QmlValueType *writeBack = 0; if (ep) { - writeBack = ep->valueTypes[valueTypeId]; + writeBack = ep->valueTypes[core.propType]; } else { - writeBack = QmlValueTypeFactory::valueType(valueTypeId); - deleteWriteBack = true; + writeBack = QmlValueTypeFactory::valueType(core.propType); } - writeBackObj = this->object; - writeBackIdx = this->coreIdx; - writeBack->read(writeBackObj, writeBackIdx); - object = writeBack; - coreIdx = valueTypeIdx; + writeBack->read(object, core.coreIndex); + + QmlPropertyCache::Data data = core; + data.coreIndex = valueTypeCoreIdx; + data.propType = valueTypePropType; + write(writeBack, data, value, context); + + writeBack->write(object, core.coreIndex); + if (!ep) delete writeBack; + + } else { + + write(object, core, value, context); + } +} - QMetaProperty prop = object->metaObject()->property(coreIdx); +void QmlMetaPropertyPrivate::write(QObject *object, const QmlPropertyCache::Data &property, + const QVariant &value, QmlContext *context) +{ + int coreIdx = property.coreIndex; - if (prop.isEnumType()) { + if (property.flags & QmlPropertyCache::Data::IsEnumType) { + QMetaProperty prop = object->metaObject()->property(property.coreIndex); QVariant v = value; - if (value.type() == QVariant::Double) { //enum values come through the script engine as doubles + // Enum values come through the script engine as doubles + if (value.type() == QVariant::Double) { double integral; double fractional = modf(value.toDouble(), &integral); if (qFuzzyCompare(fractional, (double)0.0)) @@ -784,19 +805,36 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, } prop.write(object, v); - if (writeBack) { - writeBack->write(writeBackObj, writeBackIdx); - if (deleteWriteBack) delete writeBack; - } return; } - int t = propertyType(); + int t = property.propType; int vt = value.userType(); - int category = propertyCategory(); - if (vt == t - && t != QVariant::Url) { // always resolve relative urls + if (t == QVariant::Url) { + + QUrl u; + bool found = false; + if (vt == QVariant::Url) { + u = value.toUrl(); + found = true; + } else if (vt == QVariant::ByteArray) { + u = QUrl(QLatin1String(value.toByteArray())); + found = true; + } else if (vt == QVariant::String) { + u = QUrl(value.toString()); + found = true; + } + + if (found) { + if (context && u.isRelative() && !u.isEmpty()) + u = context->baseUrl().resolved(u); + void *a[1]; + a[0] = &u; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + } + + } else if (vt == t) { void *a[1]; a[0] = (void *)value.constData(); @@ -804,9 +842,11 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, } else if (qMetaTypeId<QVariant>() == t) { - prop.write(object, value); + void *a[1]; + a[0] = (void *)&value; + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - } else if (category == QmlMetaProperty::Object) { + } else if (property.flags & QmlPropertyCache::Data::IsQObjectDerived) { QObject *o = QmlMetaType::toQObject(value); @@ -833,9 +873,10 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, } - } else if (category == QmlMetaProperty::List) { + } else if (property.flags & QmlPropertyCache::Data::IsQList) { int listType = QmlMetaType::listType(t); + QMetaProperty prop = object->metaObject()->property(property.coreIndex); if (value.userType() == qMetaTypeId<QList<QObject *> >()) { const QList<QObject *> &list = @@ -853,9 +894,11 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, QmlMetaType::clear(listVar); QmlMetaType::append(listVar, value); } - } else if (category == QmlMetaProperty::QmlList) { + + } else if (property.flags & QmlPropertyCache::Data::IsQmlList) { // XXX - optimize! + QMetaProperty prop = object->metaObject()->property(property.coreIndex); QVariant list = prop.read(object); QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)list.constData(); @@ -885,118 +928,25 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value, void *d = (void *)&obj; li->append(d); } - } else if (category == QmlMetaProperty::Normal) { - - bool found = false; - switch(t) { - case QVariant::Double: - { - double d; - if (vt == QVariant::Int) { - d = value.toInt(); - found = true; - } else if (vt == QVariant::UInt) { - d = value.toUInt(); - found = true; - } - - if (found) { - void *a[1]; - a[0] = &d; - QMetaObject::metacall(object, - QMetaObject::WriteProperty, - coreIdx, a); - } - } - break; - - case QVariant::Int: - { - int i; - if (vt == QVariant::Double) { - i = (int)value.toDouble(); - found = true; - } else if (vt == QVariant::UInt) { - i = (int)value.toUInt(); - found = true; - } - - if (found) { - void *a[1]; - a[0] = &i; - QMetaObject::metacall(object, - QMetaObject::WriteProperty, - coreIdx, a); - } - } - break; - - case QVariant::String: - { - QString s; - if (vt == QVariant::ByteArray) { - s = QLatin1String(value.toByteArray()); - found = true; - } - - if (found) { - void *a[1]; - a[0] = &s; - QMetaObject::metacall(object, - QMetaObject::WriteProperty, - coreIdx, a); - } - } - break; - - case QVariant::Url: - { - QUrl u; - if (vt == QVariant::Url) { - u = value.toUrl(); - found = true; - } else if (vt == QVariant::ByteArray) { - u = QUrl(QLatin1String(value.toByteArray())); - found = true; - } else if (vt == QVariant::String) { - u = QUrl(value.toString()); - found = true; - } + } else { + Q_ASSERT(vt != t); - if (found) { - if (context && u.isRelative() && !u.isEmpty()) - u = context->baseUrl().resolved(u); + QVariant v = value; + if (v.convert((QVariant::Type)t)) { + void *a[1]; + a[0] = (void *)v.constData(); + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + } else if ((uint)t >= QVariant::UserType && vt == QVariant::String) { + QmlMetaType::StringConverter con = QmlMetaType::customStringConverter(t); + if (con) { + QVariant v = con(value.toString()); + if (v.userType() == t) { void *a[1]; - a[0] = &u; - QMetaObject::metacall(object, - QMetaObject::WriteProperty, - coreIdx, a); - } - - } - break; - - - default: - { - if ((uint)t >= QVariant::UserType && vt == QVariant::String) { - QmlMetaType::StringConverter con = QmlMetaType::customStringConverter(t); - if (con) { - QVariant v = con(value.toString()); - prop.write(object, v); - found = true; - } + a[0] = (void *)v.constData(); + QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); } } - break; } - if (!found) - prop.write(object, value); - } - - if (writeBack) { - writeBack->write(writeBackObj, writeBackIdx); - if (deleteWriteBack) delete writeBack; } } @@ -1017,7 +967,7 @@ void QmlMetaProperty::write(const QVariant &value, WriteSource source) const d->writeSignalProperty(value); - } else if (d->coreIdx != -1) { + } else if (d->core.isValid()) { d->writeValueProperty(value, source); @@ -1030,7 +980,7 @@ void QmlMetaProperty::write(const QVariant &value, WriteSource source) const bool QmlMetaProperty::hasChangedNotifier() const { if (type() & Property && !(type() & Attached) && d->object) { - return d->object->metaObject()->property(d->coreIdx).hasNotifySignal(); + return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); } return false; } @@ -1061,7 +1011,7 @@ bool QmlMetaProperty::connectNotifier(QObject *dest, int method) const if (!(type() & Property) || (type() & Attached) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->coreIdx); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); if (prop.hasNotifySignal()) { return QMetaObject::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); } else { @@ -1082,7 +1032,7 @@ bool QmlMetaProperty::connectNotifier(QObject *dest, const char *slot) const if (!(type() & Property) || (type() & Attached) || !d->object) return false; - QMetaProperty prop = d->object->metaObject()->property(d->coreIdx); + QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); if (prop.hasNotifySignal()) { QByteArray signal(QByteArray("2") + prop.notifySignal().signature()); return QObject::connect(d->object, signal.constData(), dest, slot); @@ -1096,7 +1046,7 @@ bool QmlMetaProperty::connectNotifier(QObject *dest, const char *slot) const */ int QmlMetaProperty::coreIndex() const { - return d->coreIdx; + return d->core.coreIndex; } Q_GLOBAL_STATIC(QmlValueTypeFactory, qmlValueTypes); @@ -1111,17 +1061,17 @@ quint32 QmlMetaProperty::save() const if (type() & Attached) { rv = d->attachedFunc; } else if (type() != Invalid) { - rv = d->coreIdx; + rv = d->core.coreIndex; } Q_ASSERT(rv <= 0x7FF); Q_ASSERT(type() <= 0x3F); - Q_ASSERT(d->valueTypeIdx <= 0x7F); + Q_ASSERT(d->valueTypeCoreIdx <= 0x7F); rv |= (type() << 18); if (type() & ValueTypeProperty) - rv |= (d->valueTypeIdx << 11); + rv |= (d->valueTypeCoreIdx << 11); return rv; } @@ -1162,12 +1112,12 @@ void QmlMetaProperty::restore(quint32 id, QObject *obj, QmlContext *ctxt) d->context = ctxt; id &= 0xFFFFFF; - d->type = id >> 18; + uint type = id >> 18; id &= 0xFFFF; - if (d->type & Attached) { + if (type & Attached) { d->attachedFunc = id; - } else if (d->type & ValueTypeProperty) { + } else if (type & ValueTypeProperty) { int coreIdx = id & 0x7FF; int valueTypeIdx = id >> 11; @@ -1178,47 +1128,31 @@ void QmlMetaProperty::restore(quint32 id, QObject *obj, QmlContext *ctxt) QMetaProperty p2(valueType->metaObject()->property(valueTypeIdx)); - d->name = QLatin1String(p2.name()); - d->propType = p2.userType(); - d->coreIdx = coreIdx; - d->valueTypeIdx = valueTypeIdx; - d->valueTypeId = p.type(); - - } else if (d->type & Property) { + d->core.load(p); + d->valueTypeCoreIdx = valueTypeIdx; + d->valueTypePropType = p2.userType(); + } else if (type & Property) { - QmlMetaObjectCache *cache = QmlEnginePrivate::cache(enginePrivate, obj); - - d->coreIdx = id; + QmlPropertyCache *cache = enginePrivate?enginePrivate->cache(obj):0; if (cache) { - QmlMetaObjectCache::Data *data = - cache->property(id, obj->metaObject()); - d->propType = data->propType; - d->name = data->name; + QmlPropertyCache::Data *data = cache->property(id); + if (data) d->core = *data; } else { QMetaProperty p(obj->metaObject()->property(id)); - d->name = QLatin1String(p.name()); - d->propType = p.userType(); + d->core.load(p); } - } else if (d->type & SignalProperty) { - d->signal = obj->metaObject()->method(id); - d->coreIdx = id; + } else if (type & SignalProperty) { + + QMetaMethod method = obj->metaObject()->method(id); + d->core.load(method); } else { *this = QmlMetaProperty(); } } /*! - Return the QMetaMethod for this property if it is a SignalProperty, - otherwise returns an invalid QMetaMethod. -*/ -QMetaMethod QmlMetaProperty::method() const -{ - return d->signal; -} - -/*! \internal Creates a QmlMetaProperty for the property \a name of \a obj. Unlike @@ -1245,17 +1179,11 @@ QmlMetaProperty QmlMetaProperty::createProperty(QObject *obj, int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); if (idx == -1) return QmlMetaProperty(); + QMetaProperty vtProp = typeObject->metaObject()->property(idx); - QmlMetaProperty p; - p.d->name = pathName + QLatin1String(".") + path.last(); - p.d->context = 0; - p.d->coreIdx = prop.coreIndex(); - p.d->valueTypeIdx = idx; - p.d->valueTypeId = prop.propertyType(); - p.d->type = QmlMetaProperty::ValueTypeProperty | - QmlMetaProperty::Property; - p.d->object = obj; - p.d->propType = typeObject->metaObject()->property(idx).userType(); + QmlMetaProperty p = prop; + p.d->valueTypeCoreIdx = idx; + p.d->valueTypePropType = vtProp.userType(); return p; } diff --git a/src/declarative/qml/qmlmetaproperty_p.h b/src/declarative/qml/qmlmetaproperty_p.h index 1ccf913..00b9c3a 100644 --- a/src/declarative/qml/qmlmetaproperty_p.h +++ b/src/declarative/qml/qmlmetaproperty_p.h @@ -54,73 +54,54 @@ // #include "qmlmetaproperty.h" -#include "private/qobject_p.h" +#include <private/qobject_p.h> +#include <private/qmlpropertycache_p.h> QT_BEGIN_NAMESPACE -class QmlMetaObjectCache -{ -public: - QmlMetaObjectCache(); - ~QmlMetaObjectCache(); - - struct Data { - int propType; - int coreIndex; - QString name; - }; - - Data *property(const QString &, const QMetaObject *); - Data *property(int, const QMetaObject *); - -private: - void init(const QMetaObject *); - - Data *propertyCache; - QHash<QString, int> propertyNameCache; -}; - class QmlContext; class QmlMetaPropertyPrivate { public: QmlMetaPropertyPrivate() - : q(0), context(0), coreIdx(-1), valueTypeIdx(-1), valueTypeId(0), - type(QmlMetaProperty::Invalid), attachedFunc(-1), - object(0), propType(-1), category(QmlMetaProperty::Unknown) {} + : q(0), context(0), object(0), isDefaultProperty(false), valueTypeCoreIdx(-1), + valueTypePropType(0), attachedFunc(-1) {} + QmlMetaPropertyPrivate(const QmlMetaPropertyPrivate &other) - : q(0), name(other.name), signal(other.signal), context(other.context), - coreIdx(other.coreIdx), valueTypeIdx(other.valueTypeIdx), - valueTypeId(other.valueTypeId), type(other.type), - attachedFunc(other.attachedFunc), object(other.object), - propType(other.propType), category(other.category) {} + : q(0), context(other.context), object(other.object), + isDefaultProperty(other.isDefaultProperty), core(other.core), + valueTypeCoreIdx(other.valueTypeCoreIdx), + valueTypePropType(other.valueTypePropType), attachedFunc(other.attachedFunc) {} QmlMetaProperty *q; - - QString name; - QMetaMethod signal; QmlContext *context; - int coreIdx; - int valueTypeIdx; - int valueTypeId; - uint type; - int attachedFunc; QGuard<QObject> object; - int propType; - mutable QmlMetaProperty::PropertyCategory category; + bool isDefaultProperty; + QmlPropertyCache::Data core; + + // Describes the "virtual" value-type sub-property. + int valueTypeCoreIdx; // The prop index of the access property on the value type wrapper + int valueTypePropType; // The QVariant::Type of access property on the value type wrapper + + // The attached property accessor + int attachedFunc; void initProperty(QObject *obj, const QString &name); void initDefault(QObject *obj); QObject *attachedObject() const; - void findSignalInt(QObject *, const QString &); + QMetaMethod findSignal(QObject *, const QString &); int propertyType() const; QmlMetaProperty::PropertyCategory propertyCategory() const; void writeSignalProperty(const QVariant &); + + QVariant readValueProperty(); void writeValueProperty(const QVariant &, QmlMetaProperty::WriteSource); + static void write(QObject *, const QmlPropertyCache::Data &, const QVariant &, QmlContext *); + static QmlAbstractBinding *setBinding(QObject *, const QmlPropertyCache::Data &, QmlAbstractBinding *); static quint32 saveValueType(int, int); static quint32 saveProperty(int); diff --git a/src/declarative/qml/qmlmetatype.cpp b/src/declarative/qml/qmlmetatype.cpp index 14d85ff..c40232f 100644 --- a/src/declarative/qml/qmlmetatype.cpp +++ b/src/declarative/qml/qmlmetatype.cpp @@ -929,6 +929,30 @@ QList<QmlType*> QmlMetaType::qmlTypes() return data->nameToType.values(); } +#include <QtGui/qfont.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qbrush.h> +#include <QtGui/qcolor.h> +#include <QtGui/qpalette.h> +#include <QtGui/qicon.h> +#include <QtGui/qimage.h> +#include <QtGui/qpolygon.h> +#include <QtGui/qregion.h> +#include <QtGui/qbitmap.h> +#include <QtGui/qcursor.h> +#include <QtGui/qsizepolicy.h> +#include <QtGui/qkeysequence.h> +#include <QtGui/qpen.h> +//#include <QtGui/qtextlength.h> +#include <QtGui/qtextformat.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qtransform.h> +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> + /*! Copies \a copy into \a data, assuming they both are of type \a type. If \a copy is zero, a default type is copied. Returns true if the copy was @@ -988,7 +1012,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QChar: *static_cast<NS(QChar) *>(data) = *static_cast<const NS(QChar)*>(copy); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QVariantMap: *static_cast<NS(QVariantMap) *>(data) = *static_cast<const NS(QVariantMap)*>(copy); return true; @@ -998,7 +1021,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QVariantList: *static_cast<NS(QVariantList) *>(data) = *static_cast<const NS(QVariantList)*>(copy); return true; -#endif case QMetaType::QByteArray: *static_cast<NS(QByteArray) *>(data) = *static_cast<const NS(QByteArray)*>(copy); return true; @@ -1008,11 +1030,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QStringList: *static_cast<NS(QStringList) *>(data) = *static_cast<const NS(QStringList)*>(copy); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QBitArray: *static_cast<NS(QBitArray) *>(data) = *static_cast<const NS(QBitArray)*>(copy); return true; -#endif case QMetaType::QDate: *static_cast<NS(QDate) *>(data) = *static_cast<const NS(QDate)*>(copy); return true; @@ -1022,15 +1042,12 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QDateTime: *static_cast<NS(QDateTime) *>(data) = *static_cast<const NS(QDateTime)*>(copy); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QUrl: *static_cast<NS(QUrl) *>(data) = *static_cast<const NS(QUrl)*>(copy); return true; -#endif case QMetaType::QLocale: *static_cast<NS(QLocale) *>(data) = *static_cast<const NS(QLocale)*>(copy); return true; -#ifndef QT_NO_GEOM_VARIANT case QMetaType::QRect: *static_cast<NS(QRect) *>(data) = *static_cast<const NS(QRect)*>(copy); return true; @@ -1058,7 +1075,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QVector3D: *static_cast<NS(QVector3D) *>(data) = *static_cast<const NS(QVector3D)*>(copy); return true; -#endif #ifndef QT_NO_REGEXP case QMetaType::QRegExp: *static_cast<NS(QRegExp) *>(data) = *static_cast<const NS(QRegExp)*>(copy); @@ -1066,8 +1082,90 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) #endif case QMetaType::Void: return true; + + +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: + *static_cast<NS(QColorGroup) *>(data) = *static_cast<const NS(QColorGroup)*>(copy); + return true; +#endif + + case QMetaType::QFont: + *static_cast<NS(QFont) *>(data) = *static_cast<const NS(QFont)*>(copy); + return true; + case QMetaType::QPixmap: + *static_cast<NS(QPixmap) *>(data) = *static_cast<const NS(QPixmap)*>(copy); + return true; + case QMetaType::QBrush: + *static_cast<NS(QBrush) *>(data) = *static_cast<const NS(QBrush)*>(copy); + return true; + case QMetaType::QColor: + *static_cast<NS(QColor) *>(data) = *static_cast<const NS(QColor)*>(copy); + return true; + case QMetaType::QPalette: + *static_cast<NS(QPalette) *>(data) = *static_cast<const NS(QPalette)*>(copy); + return true; + case QMetaType::QIcon: + *static_cast<NS(QIcon) *>(data) = *static_cast<const NS(QIcon)*>(copy); + return true; + case QMetaType::QImage: + *static_cast<NS(QImage) *>(data) = *static_cast<const NS(QImage)*>(copy); + return true; + case QMetaType::QPolygon: + *static_cast<NS(QPolygon) *>(data) = *static_cast<const NS(QPolygon)*>(copy); + return true; + case QMetaType::QRegion: + *static_cast<NS(QRegion) *>(data) = *static_cast<const NS(QRegion)*>(copy); + return true; + case QMetaType::QBitmap: + *static_cast<NS(QBitmap) *>(data) = *static_cast<const NS(QBitmap)*>(copy); + return true; + case QMetaType::QCursor: + *static_cast<NS(QCursor) *>(data) = *static_cast<const NS(QCursor)*>(copy); + return true; + case QMetaType::QSizePolicy: + *static_cast<NS(QSizePolicy) *>(data) = *static_cast<const NS(QSizePolicy)*>(copy); + return true; + case QMetaType::QKeySequence: + *static_cast<NS(QKeySequence) *>(data) = *static_cast<const NS(QKeySequence)*>(copy); + return true; + case QMetaType::QPen: + *static_cast<NS(QPen) *>(data) = *static_cast<const NS(QPen)*>(copy); + return true; + case QMetaType::QTextLength: + *static_cast<NS(QTextLength) *>(data) = *static_cast<const NS(QTextLength)*>(copy); + return true; + case QMetaType::QTextFormat: + *static_cast<NS(QTextFormat) *>(data) = *static_cast<const NS(QTextFormat)*>(copy); + return true; + case QMetaType::QMatrix: + *static_cast<NS(QMatrix) *>(data) = *static_cast<const NS(QMatrix)*>(copy); + return true; + case QMetaType::QTransform: + *static_cast<NS(QTransform) *>(data) = *static_cast<const NS(QTransform)*>(copy); + return true; + case QMetaType::QMatrix4x4: + *static_cast<NS(QMatrix4x4) *>(data) = *static_cast<const NS(QMatrix4x4)*>(copy); + return true; + case QMetaType::QVector2D: + *static_cast<NS(QVector2D) *>(data) = *static_cast<const NS(QVector2D)*>(copy); + return true; + case QMetaType::QVector4D: + *static_cast<NS(QVector4D) *>(data) = *static_cast<const NS(QVector4D)*>(copy); + return true; + case QMetaType::QQuaternion: + *static_cast<NS(QQuaternion) *>(data) = *static_cast<const NS(QQuaternion)*>(copy); + return true; + default: - ; + if (type == qMetaTypeId<QVariant>()) { + *static_cast<NS(QVariant) *>(data) = *static_cast<const NS(QVariant)*>(copy); + return true; + } else if (typeCategory(type) != Unknown) { + *static_cast<void **>(data) = *static_cast<void* const *>(copy); + return true; + } + break; } } else { switch(type) { @@ -1118,7 +1216,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QChar: *static_cast<NS(QChar) *>(data) = NS(QChar)(); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QVariantMap: *static_cast<NS(QVariantMap) *>(data) = NS(QVariantMap)(); return true; @@ -1128,7 +1225,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QVariantList: *static_cast<NS(QVariantList) *>(data) = NS(QVariantList)(); return true; -#endif case QMetaType::QByteArray: *static_cast<NS(QByteArray) *>(data) = NS(QByteArray)(); return true; @@ -1138,11 +1234,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QStringList: *static_cast<NS(QStringList) *>(data) = NS(QStringList)(); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QBitArray: *static_cast<NS(QBitArray) *>(data) = NS(QBitArray)(); return true; -#endif case QMetaType::QDate: *static_cast<NS(QDate) *>(data) = NS(QDate)(); return true; @@ -1152,15 +1246,12 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QDateTime: *static_cast<NS(QDateTime) *>(data) = NS(QDateTime)(); return true; -#ifndef QT_BOOTSTRAPPED case QMetaType::QUrl: *static_cast<NS(QUrl) *>(data) = NS(QUrl)(); return true; -#endif case QMetaType::QLocale: *static_cast<NS(QLocale) *>(data) = NS(QLocale)(); return true; -#ifndef QT_NO_GEOM_VARIANT case QMetaType::QRect: *static_cast<NS(QRect) *>(data) = NS(QRect)(); return true; @@ -1188,7 +1279,6 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) case QMetaType::QVector3D: *static_cast<NS(QVector3D) *>(data) = NS(QVector3D)(); return true; -#endif #ifndef QT_NO_REGEXP case QMetaType::QRegExp: *static_cast<NS(QRegExp) *>(data) = NS(QRegExp)(); @@ -1196,8 +1286,88 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) #endif case QMetaType::Void: return true; + +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: + *static_cast<NS(QColorGroup) *>(data) = NS(QColorGroup)(); + return true; +#endif + + case QMetaType::QFont: + *static_cast<NS(QFont) *>(data) = NS(QFont)(); + return true; + case QMetaType::QPixmap: + *static_cast<NS(QPixmap) *>(data) = NS(QPixmap)(); + return true; + case QMetaType::QBrush: + *static_cast<NS(QBrush) *>(data) = NS(QBrush)(); + return true; + case QMetaType::QColor: + *static_cast<NS(QColor) *>(data) = NS(QColor)(); + return true; + case QMetaType::QPalette: + *static_cast<NS(QPalette) *>(data) = NS(QPalette)(); + return true; + case QMetaType::QIcon: + *static_cast<NS(QIcon) *>(data) = NS(QIcon)(); + return true; + case QMetaType::QImage: + *static_cast<NS(QImage) *>(data) = NS(QImage)(); + return true; + case QMetaType::QPolygon: + *static_cast<NS(QPolygon) *>(data) = NS(QPolygon)(); + return true; + case QMetaType::QRegion: + *static_cast<NS(QRegion) *>(data) = NS(QRegion)(); + return true; + case QMetaType::QBitmap: + *static_cast<NS(QBitmap) *>(data) = NS(QBitmap)(); + return true; + case QMetaType::QCursor: + *static_cast<NS(QCursor) *>(data) = NS(QCursor)(); + return true; + case QMetaType::QSizePolicy: + *static_cast<NS(QSizePolicy) *>(data) = NS(QSizePolicy)(); + return true; + case QMetaType::QKeySequence: + *static_cast<NS(QKeySequence) *>(data) = NS(QKeySequence)(); + return true; + case QMetaType::QPen: + *static_cast<NS(QPen) *>(data) = NS(QPen)(); + return true; + case QMetaType::QTextLength: + *static_cast<NS(QTextLength) *>(data) = NS(QTextLength)(); + return true; + case QMetaType::QTextFormat: + *static_cast<NS(QTextFormat) *>(data) = NS(QTextFormat)(); + return true; + case QMetaType::QMatrix: + *static_cast<NS(QMatrix) *>(data) = NS(QMatrix)(); + return true; + case QMetaType::QTransform: + *static_cast<NS(QTransform) *>(data) = NS(QTransform)(); + return true; + case QMetaType::QMatrix4x4: + *static_cast<NS(QMatrix4x4) *>(data) = NS(QMatrix4x4)(); + return true; + case QMetaType::QVector2D: + *static_cast<NS(QVector2D) *>(data) = NS(QVector2D)(); + return true; + case QMetaType::QVector4D: + *static_cast<NS(QVector4D) *>(data) = NS(QVector4D)(); + return true; + case QMetaType::QQuaternion: + *static_cast<NS(QQuaternion) *>(data) = NS(QQuaternion)(); + return true; default: - ; + if (type == qMetaTypeId<QVariant>()) { + *static_cast<NS(QVariant) *>(data) = NS(QVariant)(); + return true; + } else if (typeCategory(type) != Unknown) { + *static_cast<void **>(data) = 0; + return true; + } + break; } } diff --git a/src/declarative/qml/qmlobjectscriptclass.cpp b/src/declarative/qml/qmlobjectscriptclass.cpp new file mode 100644 index 0000000..41573a2 --- /dev/null +++ b/src/declarative/qml/qmlobjectscriptclass.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** 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 "qmlobjectscriptclass_p.h" +#include <private/qmlengine_p.h> +#include <private/qguard_p.h> +#include <private/qmlcontext_p.h> +#include <private/qmldeclarativedata_p.h> +#include <private/qmltypenamescriptclass_p.h> +#include <QtDeclarative/qmlbinding.h> +#include <QtCore/qtimer.h> + +QT_BEGIN_NAMESPACE + +struct ObjectData : public QScriptDeclarativeClass::Object { + ObjectData(QObject *o) : object(o) {} + QGuard<QObject> object; +}; + +/* + The QmlObjectScriptClass handles property access for QObjects + via QtScript. It is also used to provide a more useful API in + QtScript for QML. + */ +QmlObjectScriptClass::QmlObjectScriptClass(QmlEngine *bindEngine) +: QScriptDeclarativeClass(QmlEnginePrivate::getScriptEngine(bindEngine)), lastData(0), + engine(bindEngine) +{ + engine = bindEngine; + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(bindEngine); + + m_destroy = scriptEngine->newFunction(destroy); + m_destroyId = createPersistentIdentifier(QLatin1String("destroy")); + m_toString = scriptEngine->newFunction(tostring); + m_toStringId = createPersistentIdentifier(QLatin1String("toString")); +} + +QmlObjectScriptClass::~QmlObjectScriptClass() +{ +} + +QScriptValue QmlObjectScriptClass::newQObject(QObject *object) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + if (!object) + return newObject(scriptEngine, this, new ObjectData(object)); + + QmlDeclarativeData *ddata = QmlDeclarativeData::get(object, true); + + if (!ddata->scriptValue.isValid()) { + ddata->scriptValue = newObject(scriptEngine, this, new ObjectData(object)); + return ddata->scriptValue; + } else if (ddata->scriptValue.engine() == QmlEnginePrivate::getScriptEngine(engine)) { + return ddata->scriptValue; + } else { + return newObject(scriptEngine, this, new ObjectData(object)); + } +} + +QObject *QmlObjectScriptClass::toQObject(const QScriptValue &value) const +{ + return value.toQObject(); +} + +QScriptClass::QueryFlags +QmlObjectScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + return queryProperty(toQObject(object), name, flags, 0); +} + +QScriptClass::QueryFlags +QmlObjectScriptClass::queryProperty(QObject *obj, const Identifier &name, + QScriptClass::QueryFlags flags, QmlContext *evalContext) +{ + Q_UNUSED(flags); + lastData = 0; + lastTNData = 0; + + if (name == m_destroyId.identifier || + name == m_toStringId.identifier) + return QScriptClass::HandlesReadAccess; + + if (!obj) + return 0; + + QmlEnginePrivate *enginePrivate = QmlEnginePrivate::get(engine); + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + QmlPropertyCache *cache = 0; + QmlDeclarativeData *ddata = QmlDeclarativeData::get(obj); + if (ddata) + cache = ddata->propertyCache; + if (!cache) { + cache = enginePrivate->cache(obj); + if (cache && ddata) { cache->addref(); ddata->propertyCache = cache; } + } + + if (cache) { + lastData = cache->property(name); + } else { + local = QmlPropertyCache::create(obj->metaObject(), toString(name)); + if (local.isValid()) + lastData = &local; + } + + if (lastData) { + QScriptClass::QueryFlags rv = QScriptClass::HandlesReadAccess; + if (lastData->flags & QmlPropertyCache::Data::IsWritable) + rv |= QScriptClass::HandlesWriteAccess; + return rv; + } + + if (!evalContext && context()) { + // Global object, QScriptContext activation object, QmlContext object + QScriptValue scopeNode = scopeChainValue(context(), -3); + Q_ASSERT(scopeNode.isValid()); + Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass); + + evalContext = enginePrivate->contextClass->contextFromValue(scopeNode); + } + + if (evalContext) { + QmlContextPrivate *cp = QmlContextPrivate::get(evalContext); + + if (cp->imports) { + QmlTypeNameCache::Data *data = cp->imports->data(name); + if (data) { + lastTNData = data; + return QScriptClass::HandlesReadAccess; + } + } + } + + return 0; +} + +QScriptValue QmlObjectScriptClass::property(Object *object, const Identifier &name) +{ + return property(toQObject(object), name); +} + +QScriptValue QmlObjectScriptClass::property(QObject *obj, const Identifier &name) +{ + if (name == m_destroyId.identifier) + return m_destroy; + else if (name == m_toStringId.identifier) + return m_toString; + + Q_ASSERT(obj); + + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + + if (lastTNData) { + + if (lastTNData->type) + return enginePriv->typeNameClass->newObject(obj, lastTNData->type); + else + return enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace); + + } else if (lastData->flags & QmlPropertyCache::Data::IsFunction) { + // ### Optimize + QScriptValue sobj = scriptEngine->newQObject(obj); + return sobj.property(toString(name)); + } else { + if (!(lastData->flags & QmlPropertyCache::Data::IsConstant)) { + enginePriv->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex); + } + + if ((uint)lastData->propType < QVariant::UserType) { + QmlValueType *valueType = enginePriv->valueTypes[lastData->propType]; + if (valueType) + return enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType); + } + + if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { + QObject *rv = 0; + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return newQObject(rv); + } else { + QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); + return enginePriv->scriptValueFromVariant(var); + } + + } +} + +void QmlObjectScriptClass::setProperty(Object *object, + const Identifier &name, + const QScriptValue &value) +{ + return setProperty(toQObject(object), name, value); +} + +void QmlObjectScriptClass::setProperty(QObject *obj, + const Identifier &name, + const QScriptValue &value) +{ + Q_UNUSED(name); + + Q_ASSERT(obj); + Q_ASSERT(lastData); + + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + + // ### Can well known types be optimized? + QVariant v = QmlScriptClass::toVariant(engine, value); + delete QmlMetaPropertyPrivate::setBinding(obj, *lastData, 0); + QmlMetaPropertyPrivate::write(obj, *lastData, v, enginePriv->currentExpression->context()); +} + +QObject *QmlObjectScriptClass::toQObject(Object *object, bool *ok) +{ + if (ok) *ok = true; + + ObjectData *data = (ObjectData*)object; + return data->object.data(); +} + +QScriptValue QmlObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *) +{ + QObject* obj = context->thisObject().toQObject(); + + QString ret; + if(obj){ + QString objectName = obj->objectName(); + + ret += QLatin1String(obj->metaObject()->className()); + ret += QLatin1String("(0x"); + ret += QString::number((quintptr)obj,16); + + if (!objectName.isEmpty()) { + ret += QLatin1String(", \""); + ret += objectName; + ret += QLatin1String("\""); + } + + ret += QLatin1String(")"); + }else{ + ret += QLatin1String("null"); + } + return QScriptValue(ret); +} + +QScriptValue QmlObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine) +{ + QObject* obj = context->thisObject().toQObject(); + if(obj){ + int delay = 0; + if(context->argumentCount() > 0) + delay = context->argument(0).toInt32(); + if (delay > 0) + QTimer::singleShot(delay, obj, SLOT(deleteLater())); + else + obj->deleteLater(); + } + return engine->nullValue(); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmlobjectscriptclass_p.h b/src/declarative/qml/qmlobjectscriptclass_p.h new file mode 100644 index 0000000..3fcf009 --- /dev/null +++ b/src/declarative/qml/qmlobjectscriptclass_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLOBJECTSCRIPTCLASS_P_H +#define QMLOBJECTSCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtScript/qscriptclass.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <private/qmlpropertycache_p.h> +#include <private/qmltypenamecache_p.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QScriptContext; +class QScriptEngine; +class QmlContext; +class QmlObjectScriptClass : public QScriptDeclarativeClass +{ +public: + QmlObjectScriptClass(QmlEngine *); + ~QmlObjectScriptClass(); + + QScriptValue newQObject(QObject *); + QObject *toQObject(const QScriptValue &) const; + + enum QueryMode { IncludeAttachedProperties, SkipAttachedProperties }; + + QScriptClass::QueryFlags queryProperty(QObject *, const Identifier &, + QScriptClass::QueryFlags flags, + QmlContext *evalContext); + QScriptValue property(QObject *, const Identifier &); + void setProperty(QObject *, const Identifier &name, const QScriptValue &); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual QScriptValue property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + virtual QObject *toQObject(Object *, bool *ok = 0); + +private: + QmlTypeNameCache::Data *lastTNData; + QmlPropertyCache::Data *lastData; + QmlPropertyCache::Data local; + + PersistentIdentifier m_destroyId; + PersistentIdentifier m_toStringId; + QScriptValue m_destroy; + QScriptValue m_toString; + + static QScriptValue tostring(QScriptContext *context, QScriptEngine *engine); + static QScriptValue destroy(QScriptContext *context, QScriptEngine *engine); + + QmlEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QMLOBJECTSCRIPTCLASS_P_H + 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/qml/qmlpropertycache.cpp b/src/declarative/qml/qmlpropertycache.cpp new file mode 100644 index 0000000..63ce00c --- /dev/null +++ b/src/declarative/qml/qmlpropertycache.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** 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 "qmlpropertycache_p.h" +#include <private/qmlengine_p.h> +#include <QtDeclarative/qmlbinding.h> + +QT_BEGIN_NAMESPACE + +void QmlPropertyCache::Data::load(const QMetaProperty &p) +{ + propType = p.userType(); + if (propType == QVariant::LastType) + propType = qMetaTypeId<QVariant>(); + coreIndex = p.propertyIndex(); + notifyIndex = p.notifySignalIndex(); + name = QLatin1String(p.name()); + + if (p.isConstant()) + flags |= Data::IsConstant; + if (p.isWritable()) + flags |= Data::IsWritable; + + if (propType == qMetaTypeId<QmlBinding *>()) { + flags |= Data::IsQmlBinding; + } else if (p.isEnumType()) { + flags |= Data::IsEnumType; + } else { + QmlMetaType::TypeCategory cat = QmlMetaType::typeCategory(propType); + if (cat == QmlMetaType::Object) + flags |= Data::IsQObjectDerived; + else if (cat == QmlMetaType::List) + flags |= Data::IsQList; + else if (cat == QmlMetaType::QmlList) + flags |= Data::IsQmlList; + } +} + +void QmlPropertyCache::Data::load(const QMetaMethod &m) +{ + name = QLatin1String(m.signature()); + int parenIdx = name.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + name = name.left(parenIdx); + + coreIndex = m.methodIndex(); + flags |= Data::IsFunction; +} + + +QmlPropertyCache::QmlPropertyCache() +{ +} + +QmlPropertyCache::~QmlPropertyCache() +{ + for (int ii = 0; ii < indexCache.count(); ++ii) + indexCache.at(ii)->release(); + + for (StringCache::ConstIterator iter = stringCache.begin(); + iter != stringCache.end(); ++iter) + (*iter)->release(); + + for (IdentifierCache::ConstIterator iter = identifierCache.begin(); + iter != identifierCache.end(); ++iter) + (*iter)->release(); +} + +QmlPropertyCache::Data QmlPropertyCache::create(const QMetaObject *metaObject, + const QString &property) +{ + Q_ASSERT(metaObject); + + QmlPropertyCache::Data rv; + + int idx = metaObject->indexOfProperty(property.toUtf8()); + if (idx != -1) { + rv.load(metaObject->property(idx)); + return rv; + } + + int methodCount = metaObject->methodCount(); + for (int ii = methodCount - 1; ii >= 0; --ii) { + QMetaMethod m = metaObject->method(ii); + QString methodName = QLatin1String(m.signature()); + + int parenIdx = methodName.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + methodName = methodName.left(parenIdx); + + if (methodName == property) { + rv.load(m); + return rv; + } + } + + return rv; +} + +// ### Optimize - check engine for the parent meta object etc. +QmlPropertyCache *QmlPropertyCache::create(QmlEngine *engine, const QMetaObject *metaObject) +{ + Q_ASSERT(engine); + Q_ASSERT(metaObject); + + QmlPropertyCache *cache = new QmlPropertyCache; + + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + + // ### The properties/methods should probably be spliced on a per-metaobject basis + int propCount = metaObject->propertyCount(); + + cache->indexCache.resize(propCount); + for (int ii = propCount - 1; ii >= 0; --ii) { + QMetaProperty p = metaObject->property(ii); + QString propName = QLatin1String(p.name()); + + RData *data = new RData; + data->identifier = enginePriv->objectClass->createPersistentIdentifier(propName); + + data->load(p); + + cache->indexCache[ii] = data; + + if (cache->stringCache.contains(propName)) + continue; + + cache->stringCache.insert(propName, data); + cache->identifierCache.insert(data->identifier.identifier, data); + data->addref(); + data->addref(); + } + + int methodCount = metaObject->methodCount(); + for (int ii = methodCount - 1; ii >= 0; --ii) { + QMetaMethod m = metaObject->method(ii); + QString methodName = QLatin1String(m.signature()); + + int parenIdx = methodName.indexOf(QLatin1Char('(')); + Q_ASSERT(parenIdx != -1); + methodName = methodName.left(parenIdx); + + if (cache->stringCache.contains(methodName)) + continue; + + RData *data = new RData; + data->identifier = enginePriv->objectClass->createPersistentIdentifier(methodName); + + data->load(m); + + cache->stringCache.insert(methodName, data); + cache->identifierCache.insert(data->identifier.identifier, data); + data->addref(); + data->addref(); + } + + return cache; +} + +QmlPropertyCache::Data * +QmlPropertyCache::property(int index) const +{ + if (index < 0 || index >= indexCache.count()) + return 0; + + return indexCache.at(index); +} + +QmlPropertyCache::Data * +QmlPropertyCache::property(const QString &str) const +{ + return stringCache.value(str); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlpropertycache_p.h b/src/declarative/qml/qmlpropertycache_p.h new file mode 100644 index 0000000..f1b1219 --- /dev/null +++ b/src/declarative/qml/qmlpropertycache_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLPROPERTYCACHE_P_H +#define QMLPROPERTYCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qmlrefcount_p.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QMetaProperty; +class QmlPropertyCache : public QmlRefCount +{ +public: + QmlPropertyCache(); + virtual ~QmlPropertyCache(); + + struct Data { + inline Data(); + inline bool operator==(const Data &); + + enum Flag { + // Can apply to all properties, except IsFunction + IsConstant = 0x00000001, + IsWritable = 0x00000002, + + // These are mutually exclusive + IsFunction = 0x00000004, + IsQObjectDerived = 0x00000008, + IsEnumType = 0x00000010, + IsQmlList = 0x00000020, + IsQList = 0x00000040, + IsQmlBinding = 0x00000080 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + bool isValid() const { return coreIndex != -1; } + + Flags flags; + int propType; + int coreIndex; + int notifyIndex; + QString name; + + void load(const QMetaProperty &); + void load(const QMetaMethod &); + }; + +#if 0 + struct ValueTypeData { + int valueTypeCoreIdx; // The prop index of the access property on the value type wrapper + int valueTypePropType; // The QVariant::Type of access property on the value type wrapper + }; +#endif + + static QmlPropertyCache *create(QmlEngine *, const QMetaObject *); + static Data create(const QMetaObject *, const QString &); + + inline Data *property(const QScriptDeclarativeClass::Identifier &id) const; + Data *property(const QString &) const; + Data *property(int) const; + +private: + struct RData : public Data, public QmlRefCount { + QScriptDeclarativeClass::PersistentIdentifier identifier; + }; + + typedef QVector<RData *> IndexCache; + typedef QHash<QString, RData *> StringCache; + typedef QHash<QScriptDeclarativeClass::Identifier, RData *> IdentifierCache; + + IndexCache indexCache; + StringCache stringCache; + IdentifierCache identifierCache; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QmlPropertyCache::Data::Flags); + +QmlPropertyCache::Data::Data() +: flags(0), propType(0), coreIndex(-1), notifyIndex(-1) +{ +} + +bool QmlPropertyCache::Data::operator==(const QmlPropertyCache::Data::Data &other) +{ + return flags == other.flags && + propType == other.propType && + coreIndex == other.coreIndex && + notifyIndex == other.notifyIndex && + name == other.name; +} + +QmlPropertyCache::Data * +QmlPropertyCache::property(const QScriptDeclarativeClass::Identifier &id) const +{ + return identifierCache.value(id); +} + +QT_END_NAMESPACE + +#endif // QMLPROPERTYCACHE_P_H diff --git a/src/declarative/qml/qmlscript.cpp b/src/declarative/qml/qmlscript.cpp new file mode 100644 index 0000000..307d72f --- /dev/null +++ b/src/declarative/qml/qmlscript.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// This is just a dummy file to include the documentation + +/*! + \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. +*/ + +/*! + \qmlproperty string Script::script + \default + JavaScript code to execute. +*/ + +/*! + \qmlproperty url Script::source + + 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/qmltypenamecache.cpp b/src/declarative/qml/qmltypenamecache.cpp new file mode 100644 index 0000000..aa1c938 --- /dev/null +++ b/src/declarative/qml/qmltypenamecache.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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 "qmltypenamecache_p.h" +#include <private/qmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QmlTypeNameCache::QmlTypeNameCache(QmlEngine *e) +: engine(e) +{ +} + +QmlTypeNameCache::~QmlTypeNameCache() +{ + qDeleteAll(stringCache); +} + +void QmlTypeNameCache::add(const QString &name, QmlType *type) +{ + if (stringCache.contains(name)) + return; + + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + + RData *data = new RData; + // ### Use typename class + data->identifier = ep->objectClass->createPersistentIdentifier(name); + data->type = type; + stringCache.insert(name, data); + identifierCache.insert(data->identifier.identifier, data); +} + +void QmlTypeNameCache::add(const QString &name, QmlTypeNameCache *typeNamespace) +{ + if (stringCache.contains(name)) + return; + + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + + RData *data = new RData; + // ### Use typename class + data->identifier = ep->objectClass->createPersistentIdentifier(name); + data->typeNamespace = typeNamespace; + stringCache.insert(name, data); + identifierCache.insert(data->identifier.identifier, data); + typeNamespace->addref(); +} + +QmlTypeNameCache::Data *QmlTypeNameCache::data(const QString &id) const +{ + return stringCache.value(id); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmltypenamecache_p.h b/src/declarative/qml/qmltypenamecache_p.h new file mode 100644 index 0000000..f11fe68 --- /dev/null +++ b/src/declarative/qml/qmltypenamecache_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLTYPENAMECACHE_P_H +#define QMLTYPENAMECACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qmlrefcount_p.h> +#include <private/qscriptdeclarativeclass_p.h> + +QT_BEGIN_NAMESPACE + +class QmlType; +class QmlEngine; +class QmlTypeNameCache : public QmlRefCount +{ +public: + QmlTypeNameCache(QmlEngine *); + virtual ~QmlTypeNameCache(); + + struct Data { + inline Data(); + inline ~Data(); + QmlType *type; + QmlTypeNameCache *typeNamespace; + }; + + void add(const QString &, QmlType *); + void add(const QString &, QmlTypeNameCache *); + + Data *data(const QString &) const; + inline Data *data(const QScriptDeclarativeClass::Identifier &id) const; + +private: + struct RData : public Data { + QScriptDeclarativeClass::PersistentIdentifier identifier; + }; + typedef QHash<QString, RData *> StringCache; + typedef QHash<QScriptDeclarativeClass::Identifier, RData *> IdentifierCache; + + StringCache stringCache; + IdentifierCache identifierCache; + QmlEngine *engine; +}; + +QmlTypeNameCache::Data::Data() +: type(0), typeNamespace(0) +{ +} + +QmlTypeNameCache::Data::~Data() +{ + if (typeNamespace) typeNamespace->release(); +} + +QmlTypeNameCache::Data *QmlTypeNameCache::data(const QScriptDeclarativeClass::Identifier &id) const +{ + return identifierCache.value(id); +} + +QT_END_NAMESPACE + +#endif // QMLTYPENAMECACHE_P_H + diff --git a/src/declarative/qml/qmltypenamescriptclass.cpp b/src/declarative/qml/qmltypenamescriptclass.cpp new file mode 100644 index 0000000..c0613d1 --- /dev/null +++ b/src/declarative/qml/qmltypenamescriptclass.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** 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 "qmltypenamescriptclass_p.h" +#include <private/qmlengine_p.h> +#include <private/qmltypenamecache_p.h> + +QT_BEGIN_NAMESPACE + +struct TypeNameData : public QScriptDeclarativeClass::Object { + TypeNameData(QObject *o, QmlType *t, QmlTypeNameScriptClass::TypeNameMode m) : object(o), type(t), typeNamespace(0), mode(m) {} + TypeNameData(QObject *o, QmlTypeNameCache *n, QmlTypeNameScriptClass::TypeNameMode m) : object(o), type(0), typeNamespace(n), mode(m) { + if (typeNamespace) typeNamespace->addref(); + } + ~TypeNameData() { + if (typeNamespace) typeNamespace->release(); + } + + QObject *object; + QmlType *type; + QmlTypeNameCache *typeNamespace; + QmlTypeNameScriptClass::TypeNameMode mode; +}; + +QmlTypeNameScriptClass::QmlTypeNameScriptClass(QmlEngine *bindEngine) +: QScriptDeclarativeClass(QmlEnginePrivate::getScriptEngine(bindEngine)), + engine(bindEngine), object(0), type(0) +{ +} + +QmlTypeNameScriptClass::~QmlTypeNameScriptClass() +{ +} + +QScriptValue QmlTypeNameScriptClass::newObject(QObject *object, QmlType *type, TypeNameMode mode) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + return QScriptDeclarativeClass::newObject(scriptEngine, this, new TypeNameData(object, type, mode)); +} + +QScriptValue QmlTypeNameScriptClass::newObject(QObject *object, QmlTypeNameCache *ns, TypeNameMode mode) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + return QScriptDeclarativeClass::newObject(scriptEngine, this, new TypeNameData(object, ns, mode)); +} + +QScriptClass::QueryFlags +QmlTypeNameScriptClass::queryProperty(Object *obj, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(flags); + + TypeNameData *data = (TypeNameData *)obj; + + object = 0; + type = 0; + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + + if (data->typeNamespace) { + + QmlTypeNameCache::Data *d = data->typeNamespace->data(name); + if (d && d->type) { + type = d->type; + return QScriptClass::HandlesReadAccess; + } else { + return 0; + } + + } else { + Q_ASSERT(data->type); + + QString strName = toString(name); + + if (strName.at(0).isUpper()) { + // Must be an enum + if (data->mode == IncludeEnums) { + // ### Optimize + const char *enumName = strName.toUtf8().constData(); + const QMetaObject *metaObject = data->type->baseMetaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + int value = e.keyToValue(enumName); + if (value != -1) { + enumValue = value; + return QScriptClass::HandlesReadAccess; + } + } + } + return 0; + } else if (data->object) { + // Must be an attached property + object = qmlAttachedPropertiesObjectById(data->type->index(), data->object); + if (!object) return 0; + return ep->objectClass->queryProperty(object, name, flags, 0); + } + } + + return 0; +} + +QScriptValue QmlTypeNameScriptClass::property(Object *obj, const Identifier &name) +{ + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + if (type) { + return newObject(((TypeNameData *)obj)->object, type, ((TypeNameData *)obj)->mode); + } else if (object) { + return ep->objectClass->property(object, name); + } else { + return QScriptValue(enumValue); + } +} + +void QmlTypeNameScriptClass::setProperty(Object *o, const Identifier &n, const QScriptValue &v) +{ + Q_ASSERT(object); + Q_ASSERT(!type); + + QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); + ep->objectClass->setProperty(((TypeNameData *)o)->object, n, v); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmltypenamescriptclass_p.h b/src/declarative/qml/qmltypenamescriptclass_p.h new file mode 100644 index 0000000..d8112d2 --- /dev/null +++ b/src/declarative/qml/qmltypenamescriptclass_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLTYPENAMESCRIPTCLASS_P_H +#define QMLTYPENAMESCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include <QtScript/qscriptclass.h> +#include <private/qscriptdeclarativeclass_p.h> +#include <private/qmlengine_p.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QmlType; +class QmlTypeNameCache; +class QmlTypeNameScriptClass : public QScriptDeclarativeClass +{ +public: + QmlTypeNameScriptClass(QmlEngine *); + ~QmlTypeNameScriptClass(); + + enum TypeNameMode { IncludeEnums, ExcludeEnums }; + QScriptValue newObject(QObject *, QmlType *, TypeNameMode = IncludeEnums); + QScriptValue newObject(QObject *, QmlTypeNameCache *, TypeNameMode = IncludeEnums); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual QScriptValue property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + +private: + QmlEngine *engine; + QObject *object; + QmlType *type; + quint32 enumValue; +}; + +QT_END_NAMESPACE + +#endif // QMLTYPENAMESCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qmlvaluetypescriptclass.cpp b/src/declarative/qml/qmlvaluetypescriptclass.cpp new file mode 100644 index 0000000..6fd674a --- /dev/null +++ b/src/declarative/qml/qmlvaluetypescriptclass.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** 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 "qmlvaluetypescriptclass_p.h" +#include <private/qmlengine_p.h> + +QT_BEGIN_NAMESPACE + +struct QmlValueTypeReference { + QmlValueType *type; + QGuard<QObject> object; + int property; +}; +Q_DECLARE_METATYPE(QmlValueTypeReference); + +QmlValueTypeScriptClass::QmlValueTypeScriptClass(QmlEngine *bindEngine) +: QScriptClass(QmlEnginePrivate::getScriptEngine(bindEngine)), engine(bindEngine) +{ +} + +QmlValueTypeScriptClass::~QmlValueTypeScriptClass() +{ +} + +QScriptValue QmlValueTypeScriptClass::newObject(QObject *object, int coreIndex, QmlValueType *type) +{ + QmlValueTypeReference ref = { type, object, coreIndex }; + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + return scriptEngine->newObject(this, scriptEngine->newVariant(qVariantFromValue(ref))); +} + +QmlValueTypeScriptClass::QueryFlags +QmlValueTypeScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(flags); + QmlValueTypeReference ref = + qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); + + if (!ref.object) + return 0; + + QByteArray propName = name.toString().toUtf8(); + + int idx = ref.type->metaObject()->indexOfProperty(propName.constData()); + if (idx == -1) + return 0; + *id = idx; + + QMetaProperty prop = ref.object->metaObject()->property(idx); + + QmlValueTypeScriptClass::QueryFlags rv = + QmlValueTypeScriptClass::HandlesReadAccess; + if (prop.isWritable()) + rv |= QmlValueTypeScriptClass::HandlesWriteAccess; + + return rv; +} + +QScriptValue QmlValueTypeScriptClass::property(const QScriptValue &object, + const QScriptString &name, + uint id) +{ + Q_UNUSED(name); + QmlValueTypeReference ref = + qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); + + if (!ref.object) + return QScriptValue(); + + ref.type->read(ref.object, ref.property); + + QMetaProperty p = ref.type->metaObject()->property(id); + QVariant rv = p.read(ref.type); + + return static_cast<QmlEnginePrivate *>(QObjectPrivate::get(engine))->scriptValueFromVariant(rv); +} + +void QmlValueTypeScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value) +{ + Q_UNUSED(name); + QmlValueTypeReference ref = + qvariant_cast<QmlValueTypeReference>(object.data().toVariant()); + + if (!ref.object) + return; + + QVariant v = QmlScriptClass::toVariant(engine, value); + + ref.type->read(ref.object, ref.property); + QMetaProperty p = ref.type->metaObject()->property(id); + p.write(ref.type, v); + ref.type->write(ref.object, ref.property); +} + +QVariant QmlValueTypeScriptClass::toVariant(const QScriptValue &val) +{ + QmlValueTypeReference ref = + qvariant_cast<QmlValueTypeReference>(val.data().toVariant()); + + if (!ref.object) + return QVariant(); + + QMetaProperty p = ref.object->metaObject()->property(ref.property); + return p.read(ref.object); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmlvaluetypescriptclass_p.h b/src/declarative/qml/qmlvaluetypescriptclass_p.h new file mode 100644 index 0000000..d656a22 --- /dev/null +++ b/src/declarative/qml/qmlvaluetypescriptclass_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLVALUETYPESCRIPTCLASS_P_H +#define QMLVALUETYPESCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include <QtScript/qscriptclass.h> +#include <private/qscriptdeclarativeclass_p.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QmlValueType; +class QmlValueTypeScriptClass : public QScriptClass +{ +public: + QmlValueTypeScriptClass(QmlEngine *); + ~QmlValueTypeScriptClass(); + + QScriptValue newObject(QObject *object, int coreIndex, QmlValueType *); + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, + uint id); + virtual void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value); + + QVariant toVariant(const QScriptValue &); +private: + QmlEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QMLVALUETYPESCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 44b17e6..e4eef64 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -143,6 +143,7 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, const QList<QmlCompiledData::CustomTypeData> &customTypeData = comp->customTypeData; const QList<int> &intData = comp->intData; const QList<float> &floatData = comp->floatData; + const QList<QmlPropertyCache *> &propertyCaches = comp->propertyCaches; QmlEnginePrivate::SimpleList<QmlAbstractBinding> bindValues; @@ -165,8 +166,8 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, if (instr.init.parserStatusSize) parserStatus = QmlEnginePrivate::SimpleList<QmlParserStatus>(instr.init.parserStatusSize); - if (instr.init.idSize) - cp->setIdPropertyCount(instr.init.idSize); + if (instr.init.contextCache != -1) + cp->setIdPropertyData(comp->contextCaches.at(instr.init.contextCache)); } break; @@ -259,9 +260,15 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, const QByteArray &metadata = datas.at(instr.storeMeta.data); QMetaObjectBuilder::fromRelocatableData(&mo, 0, metadata); - const QmlVMEMetaData *data = (const QmlVMEMetaData *)datas.at(instr.storeMeta.aliasData).constData(); + const QmlVMEMetaData *data = + (const QmlVMEMetaData *)datas.at(instr.storeMeta.aliasData).constData(); (void)new QmlVMEMetaObject(target, &mo, data, comp); + + QmlDeclarativeData *ddata = QmlDeclarativeData::get(target, true); + if (ddata->propertyCache) ddata->propertyCache->release(); + ddata->propertyCache = propertyCaches.at(instr.storeMeta.propertyCache); + ddata->propertyCache->addref(); } break; @@ -557,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/qml/qmlxmlhttprequest.cpp b/src/declarative/qml/qmlxmlhttprequest.cpp index 65c5b16..0bc927c 100644 --- a/src/declarative/qml/qmlxmlhttprequest.cpp +++ b/src/declarative/qml/qmlxmlhttprequest.cpp @@ -1190,7 +1190,7 @@ static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngin if (!request) return context->throwError(QScriptContext::ReferenceError, QLatin1String("Not an XMLHttpRequest object")); if (context->argumentCount() < 2 || context->argumentCount() > 5) - return context->throwError(QScriptContext::SyntaxError, "Incorrect argument count"); + return context->throwError(QScriptContext::SyntaxError, QLatin1String("Incorrect argument count")); // Argument 0 - Method QString method = context->argument(0).toString().toUpper(); @@ -1198,7 +1198,7 @@ static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngin method != QLatin1String("PUT") && method != QLatin1String("HEAD") && method != QLatin1String("POST")) - return context->throwError(QScriptContext::SyntaxError, "Unsupported method"); + return context->throwError(QScriptContext::SyntaxError, QLatin1String("Unsupported method")); // Argument 1 - URL @@ -1209,12 +1209,12 @@ static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngin if (ctxt) url = ctxt->resolvedUrl(url); else - return context->throwError(QScriptContext::SyntaxError, "Relative URLs not supported"); + return context->throwError(QScriptContext::SyntaxError, QLatin1String("Relative URLs not supported")); } // Argument 2 - async (optional) if (context->argumentCount() > 2 && !context->argument(2).toBoolean()) - return context->throwError(QScriptContext::SyntaxError, "Synchronous call not supported"); + return context->throwError(QScriptContext::SyntaxError, QLatin1String("Synchronous call not supported")); // Argument 3/4 - user/pass (optional) @@ -1242,12 +1242,12 @@ static QScriptValue qmlxmlhttprequest_setRequestHeader(QScriptContext *context, if (!request) return context->throwError(QScriptContext::ReferenceError, QLatin1String("Not an XMLHttpRequest object")); if (context->argumentCount() != 2) - return context->throwError(QScriptContext::SyntaxError, "Incorrect argument count"); + return context->throwError(QScriptContext::SyntaxError, QLatin1String("Incorrect argument count")); if (request->readyState() != QmlXMLHttpRequest::Opened || request->sendFlag()) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); QString name = context->argument(0).toString(); @@ -1289,10 +1289,10 @@ static QScriptValue qmlxmlhttprequest_send(QScriptContext *context, QScriptEngin if (!request) return context->throwError(QScriptContext::ReferenceError, QLatin1String("Not an XMLHttpRequest object")); if (request->readyState() != QmlXMLHttpRequest::Opened) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); if (request->sendFlag()) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); QByteArray data; if (context->argumentCount() > 0) @@ -1324,7 +1324,7 @@ static QScriptValue qmlxmlhttprequest_getResponseHeader(QScriptContext *context, if (request->readyState() != QmlXMLHttpRequest::Loading && request->readyState() != QmlXMLHttpRequest::Done && request->readyState() != QmlXMLHttpRequest::HeadersReceived) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); QString headerName = context->argument(0).toString(); @@ -1342,7 +1342,7 @@ static QScriptValue qmlxmlhttprequest_getAllResponseHeaders(QScriptContext *cont if (request->readyState() != QmlXMLHttpRequest::Loading && request->readyState() != QmlXMLHttpRequest::Done && request->readyState() != QmlXMLHttpRequest::HeadersReceived) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); return QScriptValue(request->headers()); } @@ -1363,7 +1363,7 @@ static QScriptValue qmlxmlhttprequest_status(QScriptContext *context, QScriptEng if (request->readyState() == QmlXMLHttpRequest::Unsent || request->readyState() == QmlXMLHttpRequest::Opened) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); if (request->errorFlag()) return QScriptValue(0); @@ -1378,7 +1378,7 @@ static QScriptValue qmlxmlhttprequest_statusText(QScriptContext *context, QScrip if (request->readyState() == QmlXMLHttpRequest::Unsent || request->readyState() == QmlXMLHttpRequest::Opened) - return context->throwError(INVALID_STATE_ERR, "Invalid state"); + return context->throwError(INVALID_STATE_ERR, QLatin1String("Invalid state")); if (request->errorFlag()) return QScriptValue(0); diff --git a/src/declarative/util/qmlpackage.cpp b/src/declarative/util/qmlpackage.cpp index 912bb6b..7df8453 100644 --- a/src/declarative/util/qmlpackage.cpp +++ b/src/declarative/util/qmlpackage.cpp @@ -55,11 +55,11 @@ public: class QmlPackageAttached : public QObject { Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) public: QmlPackageAttached(QObject *parent); virtual ~QmlPackageAttached(); - Q_PROPERTY(QString name READ name WRITE setName) QString name() const; void setName(const QString &n); diff --git a/src/declarative/util/qmlscript.cpp b/src/declarative/util/qmlscript.cpp deleted file mode 100644 index de2128d..0000000 --- a/src/declarative/util/qmlscript.cpp +++ /dev/null @@ -1,205 +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->pushContext(); - for (int i = context->d_func()->scopeChain.size() - 1; i > -1; --i) { - scriptContext->pushScope(context->d_func()->scopeChain.at(i)); - } - scriptContext->setActivationObject(context->d_func()->scopeChain.at(0)); - - 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(); -} - -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 \ diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index b1f36be..029d3a5 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -340,6 +340,25 @@ public: JSC::JSValue prototype; }; +class QScriptProgramPrivate +{ +public: + QScriptProgramPrivate() : refcount(1), hasException(false) { } + ~QScriptProgramPrivate() { if (evalNode) evalNode->destroyData(); } + + void addref() { ++refcount; } + void release() { if (--refcount) delete this; } + + int refcount; + + bool hasException; + QScriptValue exception; + + JSC::SourceCode source; + WTF::RefPtr<JSC::EvalNode> evalNode; +}; + + namespace QScript { @@ -2113,7 +2132,121 @@ QScriptSyntaxCheckResult QScriptEnginePrivate::checkSyntax(const QString &progra return QScriptSyntaxCheckResult(p); } +QScriptProgram QScriptEngine::compile(const QString &program, const QString &fileName, int lineNumber) +{ + Q_D(QScriptEngine); + + QScriptProgram rv; + rv.d = new QScriptProgramPrivate; + + JSC::JSLock lock(false); // ### hmmm + QBoolBlocker inEval(d->inEval, true); + currentContext()->activationObject(); //force the creation of a context for native function; + + JSC::Debugger* debugger = d->originalGlobalObject()->debugger(); + + JSC::UString jscProgram = program; + JSC::UString jscFileName = fileName; + JSC::ExecState* exec = d->currentFrame; + WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider + = QScript::UStringSourceProviderWithFeedback::create(jscProgram, jscFileName, lineNumber, d); + intptr_t sourceId = provider->asID(); + JSC::SourceCode &source = rv.d->source; + source = JSC::SourceCode(provider, lineNumber); //after construction of SourceCode provider variable will be null. + + if (debugger) + debugger->evaluateStart(sourceId); + + exec->clearException(); + JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject()); + + int errorLine; + JSC::UString errorMessage; + WTF::RefPtr<JSC::EvalNode> &evalNode = rv.d->evalNode; + evalNode = exec->globalData().parser->parse<JSC::EvalNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, source, &errorLine, &errorMessage); + if (!evalNode) { + JSC::JSValue exceptionValue = JSC::Error::create(exec, JSC::SyntaxError, errorMessage, errorLine, source.provider()->asID(), source.provider()->url()); + exec->setException(exceptionValue); + + if (debugger) { + debugger->exceptionThrow(JSC::DebuggerCallFrame(exec, exceptionValue), sourceId, false); + debugger->evaluateStop(exceptionValue, sourceId); + } + + rv.d->hasException = true; + rv.d->exception = d->scriptValueFromJSCValue(exceptionValue); + } else if (debugger) { + debugger->evaluateStop(JSC::JSValue(), sourceId); + } + + return rv; +} + +QScriptValue QScriptEngine::evaluate(const QScriptProgram &program) +{ + Q_D(QScriptEngine); + + if (0 == program.d) + return QScriptValue(); + else if (program.d->hasException) + return program.d->exception; + else if (!program.d->evalNode) + return QScriptValue(); + + JSC::JSLock lock(false); // ### hmmm + QBoolBlocker inEval(d->inEval, true); + currentContext()->activationObject(); //force the creation of a context for native function; + + JSC::Debugger* debugger = d->originalGlobalObject()->debugger(); + + JSC::ExecState* exec = d->currentFrame; + + intptr_t sourceId = program.d->source.provider()->asID(); + + if (debugger) + debugger->evaluateStart(sourceId); + + exec->clearException(); + JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject()); + + WTF::RefPtr<JSC::EvalNode> &evalNode = program.d->evalNode; + + JSC::EvalExecutable executable(exec, program.d->source); + executable.compile(exec, evalNode, exec->scopeChain()); + JSC::JSValue thisValue = d->thisForContext(exec); + JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue.toObject(exec); + JSC::JSValue exceptionValue; + d->timeoutChecker()->setShouldAbort(false); + if (d->processEventsInterval > 0) + d->timeoutChecker()->reset(); + JSC::JSValue result = exec->interpreter()->execute(&executable, exec, thisObject, exec->scopeChain(), &exceptionValue); + + if (d->timeoutChecker()->shouldAbort()) { + if (d->abortResult.isError()) + exec->setException(d->scriptValueToJSCValue(d->abortResult)); + + if (debugger) + debugger->evaluateStop(d->scriptValueToJSCValue(d->abortResult), sourceId); + + return d->abortResult; + } + + if (exceptionValue) { + exec->setException(exceptionValue); + + if (debugger) + debugger->evaluateStop(exceptionValue, sourceId); + + return d->scriptValueFromJSCValue(exceptionValue); + } + + if (debugger) + debugger->evaluateStop(result, sourceId); + + Q_ASSERT(!exec->hadException()); + return d->scriptValueFromJSCValue(result); +} /*! Evaluates \a program, using \a lineNumber as the base line number, @@ -2266,6 +2399,35 @@ QScriptContext *QScriptEngine::pushContext() return d->contextForFrame(newFrame); } +/*! + Enters a new execution context and returns the associated + QScriptContext object. + + Once you are done with the context, you should call popContext() to + restore the old context. + + By default, the `this' object of the new context is the Global Object. + The context's \l{QScriptContext::callee()}{callee}() will be invalid. + + Unlike pushContext(), the default scope chain is reset to include + only the global object and the QScriptContext's activation object. + + \sa popContext() +*/ +QScriptContext *QScriptEngine::pushCleanContext() +{ + Q_D(QScriptEngine); + + JSC::CallFrame* newFrame = d->pushContext(d->currentFrame, d->currentFrame->globalData().dynamicGlobalObject, + JSC::ArgList(), /*callee = */0, false, true); + + if (agent()) + agent()->contextPush(); + + return d->contextForFrame(newFrame); +} + + /*! \internal push a context for a native function. JSC native function doesn't have different stackframe or context. so we need to create one. @@ -2277,7 +2439,8 @@ QScriptContext *QScriptEngine::pushContext() return the new top frame. (might be the same as exec if a new stackframe was not needed) or 0 if stack overflow */ JSC::CallFrame *QScriptEnginePrivate::pushContext(JSC::CallFrame *exec, JSC::JSValue _thisObject, - const JSC::ArgList& args, JSC::JSObject *callee, bool calledAsConstructor) + const JSC::ArgList& args, JSC::JSObject *callee, bool calledAsConstructor, + bool clearScopeChain) { JSC::JSValue thisObject = _thisObject; if (calledAsConstructor) { @@ -2311,7 +2474,14 @@ JSC::CallFrame *QScriptEnginePrivate::pushContext(JSC::CallFrame *exec, JSC::JSV for (it = args.begin(); it != args.end(); ++it) newCallFrame[++dst] = *it; newCallFrame += argc + JSC::RegisterFile::CallFrameHeaderSize; - newCallFrame->init(0, /*vPC=*/0, exec->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); + + if (!clearScopeChain) { + newCallFrame->init(0, /*vPC=*/0, exec->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); + } else { + JSC::JSObject *jscObject = originalGlobalObject(); + JSC::ScopeChainNode *scn = new JSC::ScopeChainNode(0, jscObject, &exec->globalData(), jscObject); + newCallFrame->init(0, /*vPC=*/0, scn, exec, flags | ShouldRestoreCallFrame, argc, callee); + } } else { setContextFlags(newCallFrame, flags); #if ENABLE(JIT) @@ -3707,6 +3877,9 @@ QScriptValue QScriptEngine::objectById(qint64 id) const return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue((JSC::JSCell*)id); } + + + /*! \since 4.5 \class QScriptSyntaxCheckResult @@ -3835,4 +4008,51 @@ Q_AUTOTEST_EXPORT bool qt_script_isJITEnabled() } #endif +QScriptProgram::QScriptProgram() +: d(0) +{ +} + +QScriptProgram::~QScriptProgram() +{ + if (d) d->release(); +} + +QScriptProgram::QScriptProgram(const QScriptProgram &c) +: d(c.d) +{ + if (d) d->addref(); +} + +QScriptProgram &QScriptProgram::operator=(const QScriptProgram &c) +{ + if (c.d) c.d->addref(); + if (d) d->release(); + d = c.d; + return *this; +} + +bool QScriptProgram::isNull() const +{ + return d == 0; +} + +bool QScriptProgram::hasSyntaxError() const +{ + if (d) return d->hasException; + else return false; +} + +QScriptValue QScriptProgram::syntaxError() const +{ + if (d) return d->exception; + else return QScriptValue(); +} + +QString QScriptProgram::programSource() const +{ + if (d) return d->source.toString(); + else return QString(); +} + QT_END_NAMESPACE diff --git a/src/script/api/qscriptengine.h b/src/script/api/qscriptengine.h index 701f9c6..cd86aca 100644 --- a/src/script/api/qscriptengine.h +++ b/src/script/api/qscriptengine.h @@ -120,6 +120,29 @@ private: friend class QScriptEnginePrivate; }; +class QScriptProgramPrivate; +class Q_SCRIPT_EXPORT QScriptProgram +{ +public: + QScriptProgram(); + QScriptProgram(const QScriptProgram &); + ~QScriptProgram(); + + QScriptProgram &operator=(const QScriptProgram &); + + bool isNull() const; + + bool hasSyntaxError() const; + QScriptValue syntaxError() const; + + QString programSource() const; + +private: + friend class QScriptEngine; + QScriptProgramPrivate *d; +}; + +class QScriptCode; class Q_SCRIPT_EXPORT QScriptEngine #ifndef QT_NO_QOBJECT : public QObject @@ -159,11 +182,15 @@ public: QScriptContext *currentContext() const; QScriptContext *pushContext(); + QScriptContext *pushCleanContext(); void popContext(); bool canEvaluate(const QString &program) const; static QScriptSyntaxCheckResult checkSyntax(const QString &program); + QScriptProgram compile(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + QScriptValue evaluate(const QScriptProgram &); + QScriptValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); bool isEvaluating() const; diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h index 5f31054..7218ee1 100644 --- a/src/script/api/qscriptengine_p.h +++ b/src/script/api/qscriptengine_p.h @@ -162,7 +162,7 @@ public: static JSC::Register *thisRegisterForFrame(JSC::ExecState *frame); JSC::CallFrame *pushContext(JSC::CallFrame *exec, JSC::JSValue thisObject, const JSC::ArgList& args, - JSC::JSObject *callee, bool calledAsConstructor = false); + JSC::JSObject *callee, bool calledAsConstructor = false, bool clearScopeChain = false); void popContext(); void mark(JSC::MarkStack& markStack); diff --git a/src/script/api/qscriptvalue.cpp b/src/script/api/qscriptvalue.cpp index 92c987c..7136a85 100644 --- a/src/script/api/qscriptvalue.cpp +++ b/src/script/api/qscriptvalue.cpp @@ -71,6 +71,7 @@ #include "bridge/qscriptclassobject_p.h" #include "bridge/qscriptvariant_p.h" #include "bridge/qscriptqobject_p.h" +#include "bridge/qscriptdeclarativeclass_p.h" /*! \since 4.3 @@ -1529,6 +1530,8 @@ QVariant QScriptValue::toVariant() const #endif else if (isArray()) return QScriptEnginePrivate::variantListFromArray(*this); + else if (QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(*this)) + return dc->toVariant(QScriptDeclarativeClass::object(*this)); // try to convert to primitive JSC::ExecState *exec = d->engine->currentFrame; JSC::JSValue savedException; @@ -1619,6 +1622,8 @@ QObject *QScriptValue::toQObject() const if (isQObject()) { QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); return static_cast<QScript::QObjectDelegate*>(object->delegate())->value(); + } else if (QScriptDeclarativeClass *dc = QScriptDeclarativeClass::scriptClass(*this)) { + return dc->toQObject(QScriptDeclarativeClass::object(*this)); } else if (isVariant()) { QVariant var = toVariant(); int type = var.userType(); diff --git a/src/script/bridge/bridge.pri b/src/script/bridge/bridge.pri index 666a07e..09e2dfb 100644 --- a/src/script/bridge/bridge.pri +++ b/src/script/bridge/bridge.pri @@ -5,7 +5,9 @@ SOURCES += \ $$PWD/qscriptvariant.cpp \ $$PWD/qscriptqobject.cpp \ $$PWD/qscriptglobalobject.cpp \ - $$PWD/qscriptactivationobject.cpp + $$PWD/qscriptactivationobject.cpp \ + $$PWD/qscriptdeclarativeobject.cpp \ + $$PWD/qscriptdeclarativeclass.cpp HEADERS += \ $$PWD/qscriptfunction_p.h \ @@ -14,4 +16,6 @@ HEADERS += \ $$PWD/qscriptvariant_p.h \ $$PWD/qscriptqobject_p.h \ $$PWD/qscriptglobalobject_p.h \ - $$PWD/qscriptactivationobject_p.h + $$PWD/qscriptactivationobject_p.h \ + $$PWD/qscriptdeclarativeobject_p.h \ + $$PWD/qscriptdeclarativeclass_p.h diff --git a/src/script/bridge/qscriptdeclarativeclass.cpp b/src/script/bridge/qscriptdeclarativeclass.cpp new file mode 100644 index 0000000..d019839 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeclass.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptdeclarativeclass_p.h" +#include "qscriptdeclarativeobject_p.h" +#include "qscriptobject_p.h" +#include <QtScript/qscriptstring.h> +#include <QtScript/qscriptengine.h> +#include <private/qscriptengine_p.h> +#include <private/qscriptvalue_p.h> +#include <private/qscriptqobject_p.h> +#include <private/qscriptactivationobject_p.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +QScriptDeclarativeClass::PersistentIdentifier::PersistentIdentifier() +{ + new (&d) JSC::Identifier(); +} + +QScriptDeclarativeClass::PersistentIdentifier::~PersistentIdentifier() +{ + ((JSC::Identifier &)d).JSC::Identifier::~Identifier(); +} + +QScriptDeclarativeClass::PersistentIdentifier::PersistentIdentifier(const PersistentIdentifier &other) +{ + identifier = other.identifier; + new (&d) JSC::Identifier((JSC::Identifier &)(other.d)); +} + +QScriptDeclarativeClass::PersistentIdentifier & +QScriptDeclarativeClass::PersistentIdentifier::operator=(const PersistentIdentifier &other) +{ + identifier = other.identifier; + ((JSC::Identifier &)d) = (JSC::Identifier &)(other.d); + return *this; +} + +QScriptDeclarativeClass::QScriptDeclarativeClass(QScriptEngine *engine) +: d_ptr(new QScriptDeclarativeClassPrivate) +{ + Q_ASSERT(sizeof(void*) == sizeof(JSC::Identifier)); + d_ptr->q_ptr = this; + d_ptr->engine = engine; +} + +QScriptValue QScriptDeclarativeClass::newObject(QScriptEngine *engine, + QScriptDeclarativeClass *scriptClass, + Object *object) +{ + Q_ASSERT(engine); + Q_ASSERT(scriptClass); + + QScriptEnginePrivate *p = static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(engine)); + + JSC::ExecState* exec = p->currentFrame; + QScriptObject *result = new (exec) QScriptObject(p->scriptObjectStructure); + result->setDelegate(new QScript::DeclarativeObjectDelegate(scriptClass, object)); + return p->scriptValueFromJSCValue(result); +} + +QScriptDeclarativeClass *QScriptDeclarativeClass::scriptClass(const QScriptValue &v) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + if (!d || !d->isJSC() || !d->jscValue.inherits(&QScriptObject::info)) + return 0; + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return 0; + return static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->scriptClass(); +} + +QScriptDeclarativeClass::Object *QScriptDeclarativeClass::object(const QScriptValue &v) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + if (!d || !d->isJSC() || !d->jscValue.inherits(&QScriptObject::info)) + return 0; + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return 0; + return static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->object(); +} + +QScriptValue QScriptDeclarativeClass::function(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return QScriptValue(); + + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + if (QScript::isFunction(result)) + return d->engine->scriptValueFromJSCValue(result); + } + + return QScriptValue(); +} + +QScriptValue QScriptDeclarativeClass::property(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return QScriptValue(); + + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + return d->engine->scriptValueFromJSCValue(result); + } + + return QScriptValue(); +} + +/* +Returns the scope chain entry at \a index. If index is less than 0, returns +entries starting at the end. For example, scopeChainValue(context, -1) will return +the value last in the scope chain. +*/ +QScriptValue QScriptDeclarativeClass::scopeChainValue(QScriptContext *context, int index) +{ + context->activationObject(); //ensure the creation of the normal scope for native context + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + + JSC::ScopeChainNode *node = frame->scopeChain(); + JSC::ScopeChainIterator it(node); + + if (index < 0) { + int count = 0; + for (it = node->begin(); it != node->end(); ++it) + ++count; + + index = qAbs(index); + if (index > count) + return QScriptValue(); + else + index = count - index; + } + + for (it = node->begin(); it != node->end(); ++it) { + + if (index == 0) { + + JSC::JSObject *object = *it; + if (!object) return QScriptValue(); + + if (object->inherits(&QScript::QScriptActivationObject::info) + && (static_cast<QScript::QScriptActivationObject*>(object)->delegate() != 0)) { + // Return the object that property access is being delegated to + object = static_cast<QScript::QScriptActivationObject*>(object)->delegate(); + } + return engine->scriptValueFromJSCValue(object); + + } else { + --index; + } + + } + + return QScriptValue(); +} + +QScriptDeclarativeClass::~QScriptDeclarativeClass() +{ +} + +QScriptEngine *QScriptDeclarativeClass::engine() const +{ + return d_ptr->engine; +} + +QScriptDeclarativeClass::PersistentIdentifier +QScriptDeclarativeClass::createPersistentIdentifier(const QString &str) +{ + QScriptEnginePrivate *p = + static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(d_ptr->engine)); + JSC::ExecState* exec = p->currentFrame; + + PersistentIdentifier rv(true); + new (&rv.d) JSC::Identifier(exec, (UChar *)str.constData(), str.size()); + rv.identifier = (void *)((JSC::Identifier &)rv.d).ustring().rep(); + return rv; +} + +QScriptDeclarativeClass::PersistentIdentifier +QScriptDeclarativeClass::createPersistentIdentifier(const Identifier &id) +{ + QScriptEnginePrivate *p = + static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(d_ptr->engine)); + JSC::ExecState* exec = p->currentFrame; + + PersistentIdentifier rv(true); + new (&rv.d) JSC::Identifier(exec, (JSC::UString::Rep *)id); + rv.identifier = (void *)((JSC::Identifier &)rv.d).ustring().rep(); + return rv; +} + +QString QScriptDeclarativeClass::toString(const Identifier &identifier) +{ + JSC::UString::Rep *r = (JSC::UString::Rep *)identifier; + return QString((QChar *)r->data(), r->size()); +} + +QScriptClass::QueryFlags +QScriptDeclarativeClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(flags); + return 0; +} + +QScriptValue QScriptDeclarativeClass::property(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + Q_UNUSED(name); + return QScriptValue(); +} + +void QScriptDeclarativeClass::setProperty(Object *object, const Identifier &name, + const QScriptValue &value) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(value); +} + +QScriptValue::PropertyFlags +QScriptDeclarativeClass::propertyFlags(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + Q_UNUSED(name); + return 0; +} + +QStringList QScriptDeclarativeClass::propertyNames(Object *object) +{ + Q_UNUSED(object); + return QStringList(); +} + +QObject *QScriptDeclarativeClass::toQObject(Object *, bool *ok) +{ + if (ok) *ok = false; + return 0; +} + +QVariant QScriptDeclarativeClass::toVariant(Object *, bool *ok) +{ + if (ok) *ok = false; + return QVariant(); +} + +QScriptContext *QScriptDeclarativeClass::context() const +{ + return d_ptr->context; +} + diff --git a/src/script/bridge/qscriptdeclarativeclass_p.h b/src/script/bridge/qscriptdeclarativeclass_p.h new file mode 100644 index 0000000..b28209a --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeclass_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTDECLARATIVECLASS_P_H +#define QSCRIPTDECLARATIVECLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobjectdefs.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptclass.h> + +QT_BEGIN_NAMESPACE + +class QScriptDeclarativeClassPrivate; +class PersistentIdentifierPrivate; +class QScriptContext; +class Q_SCRIPT_EXPORT QScriptDeclarativeClass +{ +public: + typedef void* Identifier; + + struct Object { virtual ~Object() {} }; + + static QScriptValue newObject(QScriptEngine *, QScriptDeclarativeClass *, Object *); + static QScriptDeclarativeClass *scriptClass(const QScriptValue &); + static Object *object(const QScriptValue &); + + static QScriptValue function(const QScriptValue &, const Identifier &); + static QScriptValue property(const QScriptValue &, const Identifier &); + + static QScriptValue scopeChainValue(QScriptContext *, int index); + + class Q_SCRIPT_EXPORT PersistentIdentifier + { + public: + Identifier identifier; + + PersistentIdentifier(); + ~PersistentIdentifier(); + PersistentIdentifier(const PersistentIdentifier &other); + PersistentIdentifier &operator=(const PersistentIdentifier &other); + + private: + friend class QScriptDeclarativeClass; + PersistentIdentifier(bool) : identifier(0), d(0) {} + void *d; + }; + + QScriptDeclarativeClass(QScriptEngine *engine); + virtual ~QScriptDeclarativeClass(); + + QScriptEngine *engine() const; + + PersistentIdentifier createPersistentIdentifier(const QString &); + PersistentIdentifier createPersistentIdentifier(const Identifier &); + + QString toString(const Identifier &); + + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual QScriptValue property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + virtual QScriptValue::PropertyFlags propertyFlags(Object *, const Identifier &); + + virtual QStringList propertyNames(Object *); + + virtual QObject *toQObject(Object *, bool *ok = 0); + virtual QVariant toVariant(Object *, bool *ok = 0); + + QScriptContext *context() const; +protected: + friend class QScriptDeclarativeClassPrivate; + QScopedPointer<QScriptDeclarativeClassPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptdeclarativeobject.cpp b/src/script/bridge/qscriptdeclarativeobject.cpp new file mode 100644 index 0000000..76c2eb0 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeobject.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptdeclarativeobject_p.h" + +#include "../api/qscriptengine.h" +#include "../api/qscriptengine_p.h" +#include "../api/qscriptcontext.h" +#include "../api/qscriptcontext_p.h" +#include "../api/qscriptclass.h" +#include "../api/qscriptclasspropertyiterator.h" + +#include "Error.h" +#include "PropertyNameArray.h" + +#include <QtCore/qstringlist.h> + +Q_DECLARE_METATYPE(QScriptContext*) +Q_DECLARE_METATYPE(QScriptValue) +Q_DECLARE_METATYPE(QScriptValueList) + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +DeclarativeObjectDelegate::DeclarativeObjectDelegate(QScriptDeclarativeClass *c, + QScriptDeclarativeClass::Object *o) +: m_class(c), m_object(o) +{ +} + +DeclarativeObjectDelegate::~DeclarativeObjectDelegate() +{ + delete m_object; +} + +QScriptObjectDelegate::Type DeclarativeObjectDelegate::type() const +{ + return DeclarativeClassObject; +} + +bool DeclarativeObjectDelegate::getOwnPropertySlot(QScriptObject* object, + JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertySlot &slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScriptDeclarativeClass::Identifier identifier = (void *)propertyName.ustring().rep(); + + QScriptDeclarativeClassPrivate *p = QScriptDeclarativeClassPrivate::get(m_class); + p->context = reinterpret_cast<QScriptContext *>(exec); + QScriptClass::QueryFlags flags = + m_class->queryProperty(m_object, identifier, QScriptClass::HandlesReadAccess); + if (flags & QScriptClass::HandlesReadAccess) { + QScriptValue value = m_class->property(m_object, identifier); + p->context = 0; + slot.setValue(engine->scriptValueToJSCValue(value)); + return true; + } + p->context = 0; + + return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot); +} + +void DeclarativeObjectDelegate::put(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScriptDeclarativeClass::Identifier identifier = (void *)propertyName.ustring().rep(); + + QScriptDeclarativeClassPrivate *p = QScriptDeclarativeClassPrivate::get(m_class); + p->context = reinterpret_cast<QScriptContext *>(exec); + QScriptClass::QueryFlags flags = + m_class->queryProperty(m_object, identifier, QScriptClass::HandlesWriteAccess); + if (flags & QScriptClass::HandlesWriteAccess) { + m_class->setProperty(m_object, identifier, engine->scriptValueFromJSCValue(value)); + p->context = 0; + return; + } + p->context = 0; + + QScriptObjectDelegate::put(object, exec, propertyName, value, slot); +} + +bool DeclarativeObjectDelegate::deleteProperty(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + bool checkDontDelete) +{ + return QScriptObjectDelegate::deleteProperty(object, exec, propertyName, checkDontDelete); +} + +bool DeclarativeObjectDelegate::getPropertyAttributes(const QScriptObject* object, + JSC::ExecState *exec, + const JSC::Identifier &propertyName, + unsigned &attribs) const +{ + QScriptDeclarativeClass::Identifier identifier = (void *)propertyName.ustring().rep(); + + QScriptClass::QueryFlags flags = + m_class->queryProperty(m_object, identifier, QScriptClass::HandlesReadAccess); + if (flags & QScriptClass::HandlesReadAccess) { + QScriptValue::PropertyFlags flags = m_class->propertyFlags(m_object, identifier); + attribs = 0; + if (flags & QScriptValue::ReadOnly) + attribs |= JSC::ReadOnly; + if (flags & QScriptValue::SkipInEnumeration) + attribs |= JSC::DontEnum; + if (flags & QScriptValue::Undeletable) + attribs |= JSC::DontDelete; + if (flags & QScriptValue::PropertyGetter) + attribs |= JSC::Getter; + if (flags & QScriptValue::PropertySetter) + attribs |= JSC::Setter; + attribs |= flags & QScriptValue::UserRange; + return true; + } + return QScriptObjectDelegate::getPropertyAttributes(object, exec, propertyName, attribs); +} + +void DeclarativeObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecState *exec, + JSC::PropertyNameArray &propertyNames, + bool includeNonEnumerable) +{ + QStringList properties = m_class->propertyNames(m_object); + for (int ii = 0; ii < properties.count(); ++ii) { + const QString &name = properties.at(ii); + propertyNames.add(JSC::Identifier(exec, name)); + } + + QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, includeNonEnumerable); +} + +JSC::CallType DeclarativeObjectDelegate::getCallData(QScriptObject *object, JSC::CallData &callData) +{ + return QScriptObjectDelegate::getCallData(object, callData); +} + +JSC::ConstructType DeclarativeObjectDelegate::getConstructData(QScriptObject* object, JSC::ConstructData &constructData) +{ + return QScriptObjectDelegate::getConstructData(object, constructData); +} + +bool DeclarativeObjectDelegate::hasInstance(QScriptObject* object, JSC::ExecState *exec, + JSC::JSValue value, JSC::JSValue proto) +{ + return QScriptObjectDelegate::hasInstance(object, exec, value, proto); +} + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptdeclarativeobject_p.h b/src/script/bridge/qscriptdeclarativeobject_p.h new file mode 100644 index 0000000..c6dfb83 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeobject_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTDECLARATIVEOBJECT_P_H +#define QSCRIPTDECLARATIVEOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobjectdefs.h> + +#include "config.h" +#include "qscriptobject_p.h" +#include "qscriptdeclarativeclass_p.h" + +QT_BEGIN_NAMESPACE + +class QScriptClass; + +class QScriptDeclarativeClassPrivate +{ +public: + QScriptDeclarativeClassPrivate() : engine(0), q_ptr(0), context(0) {} + + QScriptEngine *engine; + QScriptDeclarativeClass *q_ptr; + QScriptContext *context; + + static QScriptDeclarativeClassPrivate *get(QScriptDeclarativeClass *c) { + return c->d_ptr.data(); + } +}; + +namespace QScript +{ + +class DeclarativeObjectDelegate : public QScriptObjectDelegate +{ +public: + DeclarativeObjectDelegate(QScriptDeclarativeClass *c, QScriptDeclarativeClass::Object *o); + ~DeclarativeObjectDelegate(); + + virtual Type type() const; + + QScriptDeclarativeClass *scriptClass() const { return m_class; } + QScriptDeclarativeClass::Object *object() const { return m_object; } + + virtual bool getOwnPropertySlot(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual void put(QScriptObject*, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + bool checkDontDelete = true); + virtual bool getPropertyAttributes(const QScriptObject*, JSC::ExecState*, + const JSC::Identifier&, + unsigned&) const; + virtual void getOwnPropertyNames(QScriptObject*, JSC::ExecState*, + JSC::PropertyNameArray&, + bool includeNonEnumerable = false); + + virtual JSC::CallType getCallData(QScriptObject*, JSC::CallData&); + virtual JSC::ConstructType getConstructData(QScriptObject*, JSC::ConstructData&); + + virtual bool hasInstance(QScriptObject*, JSC::ExecState*, + JSC::JSValue value, JSC::JSValue proto); + +private: + QScriptDeclarativeClass *m_class; + QScriptDeclarativeClass::Object *m_object; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptobject_p.h b/src/script/bridge/qscriptobject_p.h index c1cee31..a5858ac 100644 --- a/src/script/bridge/qscriptobject_p.h +++ b/src/script/bridge/qscriptobject_p.h @@ -127,7 +127,8 @@ public: enum Type { QtObject, Variant, - ClassObject + ClassObject, + DeclarativeClassObject }; QScriptObjectDelegate(); diff --git a/tests/auto/declarative/qmlecmascript/data/ScopeObject.qml b/tests/auto/declarative/qmlecmascript/data/ScopeObject.qml new file mode 100644 index 0000000..b7bec63 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/ScopeObject.qml @@ -0,0 +1,14 @@ +import Qt 4.6 + +Item { + property int a: 3 + property int binding: myFunction(); + property int binding2: myCompFunction(); + + Script { + function myCompFunction() { + return a; + } + } +} + diff --git a/tests/auto/declarative/qmlecmascript/data/attachedProperty.qml b/tests/auto/declarative/qmlecmascript/data/attachedProperty.qml new file mode 100644 index 0000000..c5088e3 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/attachedProperty.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 +import Qt.test 1.0 as Namespace + +MyQmlObject { + id: Me + property int a: MyQmlObject.value + property int b: Namespace.MyQmlObject.value + property int c: Me.Namespace.MyQmlObject.value + property int d: Me.Namespace.MyQmlObject.value +} + diff --git a/tests/auto/declarative/qmlecmascript/data/extendedObjectPropertyLookup.qml b/tests/auto/declarative/qmlecmascript/data/extendedObjectPropertyLookup.qml new file mode 100644 index 0000000..8ff3aeb --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/extendedObjectPropertyLookup.qml @@ -0,0 +1,8 @@ +import Qt.test 1.0 +import Qt 4.6 + +Object { + property MyExtendedObject a; + a: MyExtendedObject { id: Root } + property int b: Math.max(Root.extendedProperty, 0) +} diff --git a/tests/auto/declarative/qmlecmascript/data/objectsCompareAsEqual.qml b/tests/auto/declarative/qmlecmascript/data/objectsCompareAsEqual.qml new file mode 100644 index 0000000..2526576 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/objectsCompareAsEqual.qml @@ -0,0 +1,15 @@ +import Qt 4.6 + +Item { + id: Root + + property var item: Child + Item { id: Child } + + property bool test1: Child == Child + property bool test2: Child.parent == Root + property bool test3: Root != Child + property bool test4: item == Child + property bool test5: item != Root +} + diff --git a/tests/auto/declarative/qmlecmascript/data/scope.qml b/tests/auto/declarative/qmlecmascript/data/scope.qml new file mode 100644 index 0000000..80222c8 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/scope.qml @@ -0,0 +1,48 @@ +import Qt 4.6 + +Item { + id: Root + + property int a: 1 + property int binding: a + property string binding2: a + "Test" + property int binding3: myFunction() + property int binding4: myNestedFunction() + + Script { + function myFunction() { + return a; + } + } + + Item { + id: NestedObject + + Script { + function myNestedFunction() { + return a; + } + } + + property int a: 2 + property int binding: a + property string binding2: a + "Test" + property int binding3: myFunction() + property int binding4: myNestedFunction() + } + + ScopeObject { + id: CompObject + } + + property alias test1: Root.binding + property alias test2: NestedObject.binding + property alias test3: Root.binding2 + property alias test4: NestedObject.binding2 + property alias test5: Root.binding3 + property alias test6: NestedObject.binding3 + property alias test7: Root.binding4 + property alias test8: NestedObject.binding4 + property alias test9: CompObject.binding + property alias test10: CompObject.binding2 +} diff --git a/tests/auto/declarative/qmlecmascript/data/scriptAccess.js b/tests/auto/declarative/qmlecmascript/data/scriptAccess.js new file mode 100644 index 0000000..c00d285 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/scriptAccess.js @@ -0,0 +1,7 @@ +var extVariable = 19; + +function extMethod() +{ + return extVariable; +} + diff --git a/tests/auto/declarative/qmlecmascript/data/scriptAccess.qml b/tests/auto/declarative/qmlecmascript/data/scriptAccess.qml new file mode 100644 index 0000000..feb6d16 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/scriptAccess.qml @@ -0,0 +1,17 @@ +import Qt 4.6 + +Item { + Script { + function method() { + return 10; + } + } + + Script { + source: "scriptAccess.js" + } + + property int test1: method() + property int test2: extMethod() + property int test3: extVariable +} diff --git a/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.2.qml b/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.2.qml new file mode 100644 index 0000000..58cf805 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.2.qml @@ -0,0 +1,17 @@ +import Qt.test 1.0 + +MyQmlContainer { + property bool triggerDelete: false + + children: [ + MyQmlObject { + // Will trigger deletion on binding assignment + deleteOnSet: Math.max(0, 1) + }, + + MyQmlObject { + // Will trigger deletion on binding assignment, but after component creation + deleteOnSet: if (triggerDelete) 1; else 0; + } + ] +} diff --git a/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.qml b/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.qml new file mode 100644 index 0000000..074851a --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/selfDeletingBinding.qml @@ -0,0 +1,18 @@ +import Qt.test 1.0 + +MyQmlContainer { + property bool triggerDelete: false + + children: [ + MyQmlObject { + // Will trigger deletion during binding evaluation + stringProperty: {deleteMe(), "Hello"} + }, + + MyQmlObject { + // Will trigger deletion during binding evaluation, but after component creation + stringProperty: if (triggerDelete) { deleteMe(), "Hello" } else { "World" } + } + + ] +} diff --git a/tests/auto/declarative/qmlecmascript/data/signalParameterTypes.qml b/tests/auto/declarative/qmlecmascript/data/signalParameterTypes.qml new file mode 100644 index 0000000..42d26a1 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/signalParameterTypes.qml @@ -0,0 +1,16 @@ +import Qt.test 1.0 + +MyQmlObject +{ + id: Root + property int intProperty + property real realProperty + property color colorProperty + property var variantProperty + + signal mySignal(int a, real b, color c, var d) + + onMySignal: { intProperty = a; realProperty = b; colorProperty = c; variantProperty = d; } + + onBasicSignal: Root.mySignal(10, 19.2, Qt.rgba(1, 1, 0, 1), Qt.rgba(1, 0, 1, 1)) +} diff --git a/tests/auto/declarative/qmlecmascript/testtypes.h b/tests/auto/declarative/qmlecmascript/testtypes.h index ffc8fec..e6c2c20 100644 --- a/tests/auto/declarative/qmlecmascript/testtypes.h +++ b/tests/auto/declarative/qmlecmascript/testtypes.h @@ -23,6 +23,7 @@ class MyQmlObject : public QObject Q_OBJECT Q_ENUMS(MyEnum) Q_ENUMS(MyEnum2) + Q_PROPERTY(int deleteOnSet READ deleteOnSet WRITE setDeleteOnSet); Q_PROPERTY(bool trueProperty READ trueProperty CONSTANT) Q_PROPERTY(bool falseProperty READ falseProperty CONSTANT) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringChanged) @@ -61,6 +62,9 @@ public: static MyQmlAttachedObject *qmlAttachedProperties(QObject *o) { return new MyQmlAttachedObject(o); } + + int deleteOnSet() const { return 1; } + void setDeleteOnSet(int v) { if(v) delete this; } signals: void basicSignal(); void argumentSignal(int a, QString b, qreal c); @@ -68,6 +72,7 @@ signals: void objectChanged(); public slots: + void deleteMe() { delete this; } void method() { m_methodCalled = true; } void method(int a) { if(a == 163) m_methodIntCalled = true; } void setString(const QString &s) { m_string = s; } diff --git a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp index 3f01192..dde3bb7 100644 --- a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp +++ b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp @@ -45,15 +45,22 @@ private slots: void objectPropertiesTriggerReeval(); void deferredProperties(); void extensionObjects(); + void attachedProperties(); void enums(); void valueTypeFunctions(); void constantsOverrideBindings(); void outerBindingOverridesInnerBinding(); void aliasPropertyAndBinding(); void nonExistantAttachedObject(); + void scope(); + void signalParameterTypes(); + void objectsCompareAsEqual(); + void scriptAccess(); void dynamicCreation(); void dynamicDestruction(); void objectToString(); + void selfDeletingBinding(); + void extendedObjectPropertyLookup(); private: QmlEngine engine; @@ -383,6 +390,19 @@ void tst_qmlecmascript::extensionObjects() QCOMPARE(object->baseProperty(), 92); } +void tst_qmlecmascript::attachedProperties() +{ + QmlComponent component(&engine, TEST_FILE("attachedProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 19); + QCOMPARE(object->property("b").toInt(), 19); + QCOMPARE(object->property("c").toInt(), 19); + QCOMPARE(object->property("d").toInt(), 19); + + // ### Need to test attached property assignment +} + void tst_qmlecmascript::enums() { // Existant enums @@ -515,6 +535,58 @@ void tst_qmlecmascript::nonExistantAttachedObject() QVERIFY(object != 0); } +void tst_qmlecmascript::scope() +{ + QmlComponent component(&engine, TEST_FILE("scope.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 1); + QCOMPARE(object->property("test2").toInt(), 2); + QCOMPARE(object->property("test3").toString(), QString("1Test")); + QCOMPARE(object->property("test4").toString(), QString("2Test")); + QCOMPARE(object->property("test5").toInt(), 1); + QCOMPARE(object->property("test6").toInt(), 1); + QCOMPARE(object->property("test7").toInt(), 2); + QCOMPARE(object->property("test8").toInt(), 2); + QCOMPARE(object->property("test9").toInt(), 1); + QCOMPARE(object->property("test10").toInt(), 3); +} + +/* +Tests that "any" type passes through a synthesized signal parameter. This +is essentially a test of QmlMetaType::copy() +*/ +void tst_qmlecmascript::signalParameterTypes() +{ + QmlComponent component(&engine, TEST_FILE("signalParameterTypes.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + emit object->basicSignal(); + + QCOMPARE(object->property("intProperty").toInt(), 10); + QCOMPARE(object->property("realProperty").toReal(), 19.2); + QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255)); + QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255))); +} + +/* +Test that two JS objects for the same QObject compare as equal. +*/ +void tst_qmlecmascript::objectsCompareAsEqual() +{ + QmlComponent component(&engine, TEST_FILE("objectsCompareAsEqual.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + QCOMPARE(object->property("test5").toBool(), true); +} + /* Confirm bindings and alias properties can coexist. @@ -536,8 +608,22 @@ void tst_qmlecmascript::aliasPropertyAndBinding() } /* - Test using createQmlObject to dynamically generate an item - Also using createComponent is tested. +Tests that only methods of Script {} blocks are exposed. +*/ +void tst_qmlecmascript::scriptAccess() +{ + QmlComponent component(&engine, TEST_FILE("scriptAccess.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 10); + QCOMPARE(object->property("test2").toInt(), 19); + QCOMPARE(object->property("test3").toInt(), 0); +} + +/* +Test using createQmlObject to dynamically generate an item +Also using createComponent is tested. */ void tst_qmlecmascript::dynamicCreation() { @@ -576,13 +662,16 @@ void tst_qmlecmascript::dynamicDestruction() QMetaObject::invokeMethod(object, "killOther"); QVERIFY(createdQmlObject); QTest::qWait(0); + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(createdQmlObject); QTest::qWait(100); + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(!createdQmlObject); QMetaObject::invokeMethod(object, "killMe"); QVERIFY(object); QTest::qWait(0); + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(!object); } @@ -595,7 +684,44 @@ void tst_qmlecmascript::objectToString() MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); QVERIFY(object != 0); QMetaObject::invokeMethod(object, "testToString"); - QVERIFY(object->stringProperty().startsWith("Qml Object, \"objName\" MyQmlObject_QML_15")); + QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_")); + QVERIFY(object->stringProperty().endsWith(", \"objName\")")); +} + +/* +Tests bindings that indirectly cause their own deletion work. + +This test is best run under valgrind to ensure no invalid memory access occur. +*/ +void tst_qmlecmascript::selfDeletingBinding() +{ + { + QmlComponent component(&engine, TEST_FILE("selfDeletingBinding.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + } + + { + QmlComponent component(&engine, TEST_FILE("selfDeletingBinding.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + } +} + +/* +Test that extended object properties can be accessed. + +This test a regression where this used to crash. The issue was specificially +for extended objects that did not include a synthesized meta object (so non-root +and no synthesiszed properties). +*/ +void tst_qmlecmascript::extendedObjectPropertyLookup() +{ + QmlComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); } QTEST_MAIN(tst_qmlecmascript) diff --git a/tests/auto/declarative/qmllanguage/data/dynamicObjectProperties.qml b/tests/auto/declarative/qmllanguage/data/dynamicObjectProperties.qml new file mode 100644 index 0000000..e69ccee --- /dev/null +++ b/tests/auto/declarative/qmllanguage/data/dynamicObjectProperties.qml @@ -0,0 +1,13 @@ +import Test 1.0 +import Qt 4.6 +import Qt 4.6 as Qt + +Object { + property Object objectProperty + property Object objectProperty2 + objectProperty2: Object {} + + property MyComponent myComponentProperty + property MyComponent myComponentProperty2 + myComponentProperty2: MyComponent {} +} diff --git a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp index 1bf98df..3825b62 100644 --- a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp +++ b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp @@ -45,6 +45,7 @@ private slots: void idProperty(); void assignSignal(); void dynamicProperties(); + void dynamicObjectProperties(); void dynamicSignalsAndSlots(); void simpleBindings(); void autoComponentCreation(); @@ -404,6 +405,19 @@ void tst_qmllanguage::dynamicProperties() QCOMPARE(object->property("variantProperty"), QVariant(12)); } +// Tests the creation and assignment of dynamic object properties +// ### Not complete +void tst_qmllanguage::dynamicObjectProperties() +{ + QmlComponent component(&engine, TEST_FILE("dynamicObjectProperties.qml")); + VERIFY_ERRORS(0); + QObject *object = component.create(); + QVERIFY(object != 0); + + QVERIFY(object->property("objectProperty") == qVariantFromValue((QObject*)0)); + QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)0)); +} + // Tests the declaration of dynamic signals and slots void tst_qmllanguage::dynamicSignalsAndSlots() { diff --git a/tools/qmlviewer/qfxtester.cpp b/tools/qmlviewer/qfxtester.cpp index 8005b0e..0ccc9c0 100644 --- a/tools/qmlviewer/qfxtester.cpp +++ b/tools/qmlviewer/qfxtester.cpp @@ -118,12 +118,14 @@ void QFxTester::imagefailure() void QFxTester::complete() { + if (options & QmlViewer::ExitOnComplete) + QApplication::exit(hasFailed?-1:0); + if (hasCompleted) return; hasCompleted = true; - if (options & QmlViewer::ExitOnComplete) - QApplication::exit(hasFailed?-1:0); - else if (options & QmlViewer::Play) + + if (options & QmlViewer::Play) qWarning("Script playback complete"); } |