Optimize default property resolution in compiler
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativeexpression.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeexpression.h"
43 #include "private/qdeclarativeexpression_p.h"
44
45 #include "private/qdeclarativeengine_p.h"
46 #include "private/qdeclarativecontext_p.h"
47 #include "private/qdeclarativerewrite_p.h"
48 #include "private/qdeclarativescriptstring_p.h"
49 #include "private/qdeclarativecompiler_p.h"
50
51 #include <QtCore/qdebug.h>
52
53 QT_BEGIN_NAMESPACE
54
55 bool QDeclarativeDelayedError::addError(QDeclarativeEnginePrivate *e)
56 {
57     if (!e) return false;
58
59     if (e->inProgressCreations == 0) return false; // Not in construction
60
61     if (prevError) return true; // Already in error chain
62
63     prevError = &e->erroredBindings;
64     nextError = e->erroredBindings;
65     e->erroredBindings = this;
66     if (nextError) nextError->prevError = &nextError;
67
68     return true;
69 }
70
71 QDeclarativeJavaScriptExpression::QDeclarativeJavaScriptExpression()
72 : m_requiresThisObject(0), m_useSharedContext(0), m_notifyOnValueChanged(0), 
73   m_scopeObject(0), m_notifyObject(0), m_notifyIndex(-1)
74 {
75 }
76
77 QDeclarativeJavaScriptExpression::~QDeclarativeJavaScriptExpression()
78 {
79 }
80
81 QDeclarativeExpressionPrivate::QDeclarativeExpressionPrivate()
82 : expressionFunctionValid(true), extractExpressionFromFunction(false), line(-1), dataRef(0)
83 {
84 }
85
86 QDeclarativeExpressionPrivate::~QDeclarativeExpressionPrivate()
87 {
88     qPersistentDispose(v8qmlscope);
89     qPersistentDispose(v8function);
90     if (dataRef) dataRef->release();
91     dataRef = 0;
92 }
93
94 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QString &expr, 
95                                          QObject *me)
96 {
97     expression = expr;
98
99     QDeclarativeAbstractExpression::setContext(ctxt);
100     setScopeObject(me);
101     expressionFunctionValid = false;
102 }
103
104 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, v8::Handle<v8::Function> func,
105                                          QObject *me)
106 {
107     QDeclarativeAbstractExpression::setContext(ctxt);
108     setScopeObject(me);
109
110     v8function = qPersistentNew<v8::Function>(func);
111     setUseSharedContext(false);
112     expressionFunctionValid = true;
113     extractExpressionFromFunction = true;
114 }
115
116 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QString &expr, bool isRewritten,
117                                          QObject *me, const QString &srcUrl, int lineNumber)
118 {
119     url = srcUrl;
120     line = lineNumber;
121
122     expression = expr;
123
124     if (!isRewritten) {
125         expressionFunctionValid = false;
126     } else {
127         v8function = evalFunction(ctxt, me, expression, url, line, &v8qmlscope);
128         expressionFunctionValid = true;
129     }
130
131     QDeclarativeAbstractExpression::setContext(ctxt);
132     setScopeObject(me);
133 }
134
135 // Callee owns the persistent handle
136 v8::Persistent<v8::Function> 
137 QDeclarativeExpressionPrivate::evalFunction(QDeclarativeContextData *ctxt, QObject *scope, 
138                                             const QString &code, const QString &filename, int line,
139                                             v8::Persistent<v8::Object> *qmlscope)
140 {
141     QDeclarativeEngine *engine = ctxt->engine;
142     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
143
144     // XXX TODO: Implement script caching, like we used to do with QScriptProgram in the
145     // QtScript days
146     v8::HandleScope handle_scope;
147     v8::Context::Scope ctxtscope(ep->v8engine()->context());
148     
149     v8::TryCatch tc;
150     v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
151     v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line);
152     v8::Local<v8::Value> result = script->Run(scopeobject);
153     if (tc.HasCaught()) return v8::Persistent<v8::Function>();
154     if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
155     return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
156 }
157
158 /*!
159     \class QDeclarativeExpression
160     \since 4.7
161     \brief The QDeclarativeExpression class evaluates JavaScript in a QML context.
162
163     For example, given a file \c main.qml like this:
164
165     \qml
166     import QtQuick 1.0
167
168     Item {
169         width: 200; height: 200
170     }
171     \endqml
172
173     The following code evaluates a JavaScript expression in the context of the
174     above QML:
175
176     \code
177     QDeclarativeEngine *engine = new QDeclarativeEngine;
178     QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml"));
179
180     QObject *myObject = component.create();
181     QDeclarativeExpression *expr = new QDeclarativeExpression(engine->rootContext(), myObject, "width * 2");
182     int result = expr->evaluate().toInt();  // result = 400
183     \endcode
184 */
185
186 static int QDeclarativeExpression_notifyIdx = -1;
187
188 /*!
189     Create an invalid QDeclarativeExpression.
190
191     As the expression will not have an associated QDeclarativeContext, this will be a
192     null expression object and its value will always be an invalid QVariant.
193  */
194 QDeclarativeExpression::QDeclarativeExpression()
195 : QObject(*new QDeclarativeExpressionPrivate, 0)
196 {
197     Q_D(QDeclarativeExpression);
198
199     if (QDeclarativeExpression_notifyIdx == -1) 
200         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
201     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
202 }
203
204 /*!  \internal */
205 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, 
206                                                QObject *object, const QString &expr, bool isRewritten,
207                                                const QString &url, int lineNumber, 
208                                                QDeclarativeExpressionPrivate &dd)
209 : QObject(dd, 0)
210 {
211     Q_D(QDeclarativeExpression);
212     d->init(ctxt, expr, isRewritten, object, url, lineNumber);
213
214     if (QDeclarativeExpression_notifyIdx == -1) 
215         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
216     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
217 }
218
219 /*!
220     Create a QDeclarativeExpression object that is a child of \a parent.
221
222     The \script provides the expression to be evaluated, the context to evaluate it in,
223     and the scope object to evaluate it with.
224
225     This constructor is functionally equivalent to the following, but in most cases
226     is more efficient.
227     \code
228     QDeclarativeExpression expression(script.context(), script.scopeObject(), script.script(), parent);
229     \endcode
230
231     \sa QDeclarativeScriptString
232 */
233 QDeclarativeExpression::QDeclarativeExpression(const QDeclarativeScriptString &script, QObject *parent)
234 : QObject(*new QDeclarativeExpressionPrivate, parent)
235 {
236     Q_D(QDeclarativeExpression);
237     bool defaultConstruction = false;
238
239     int id = script.d.data()->bindingId;
240     if (id < 0) {
241         defaultConstruction = true;
242     } else {
243         QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(script.context());
244
245         QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(script.context()->engine());
246         QDeclarativeCompiledData *cdata = 0;
247         QDeclarativeTypeData *typeData = 0;
248         if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
249             typeData = engine->typeLoader.get(ctxtdata->url);
250             cdata = typeData->compiledData();
251         }
252
253         if (cdata)
254             d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
255                     cdata->name, script.d.data()->lineNumber);
256         else
257            defaultConstruction = true;
258
259         if (typeData)
260             typeData->release();
261     }
262
263     if (defaultConstruction)
264         d->init(QDeclarativeContextData::get(script.context()), script.script(), script.scopeObject());
265
266     if (QDeclarativeExpression_notifyIdx == -1)
267         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
268     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
269 }
270
271 /*!
272     Create a QDeclarativeExpression object that is a child of \a parent.
273
274     The \a expression JavaScript will be executed in the \a ctxt QDeclarativeContext.
275     If specified, the \a scope object's properties will also be in scope during
276     the expression's execution.
277 */
278 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContext *ctxt,
279                                                QObject *scope,
280                                                const QString &expression,
281                                                QObject *parent)
282 : QObject(*new QDeclarativeExpressionPrivate, parent)
283 {
284     Q_D(QDeclarativeExpression);
285     d->init(QDeclarativeContextData::get(ctxt), expression, scope);
286
287     if (QDeclarativeExpression_notifyIdx == -1) 
288         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
289     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
290 }
291
292 /*! 
293     \internal
294 */
295 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope,
296                                                const QString &expression)
297 : QObject(*new QDeclarativeExpressionPrivate, 0)
298 {
299     Q_D(QDeclarativeExpression);
300     d->init(ctxt, expression, scope);
301
302     if (QDeclarativeExpression_notifyIdx == -1) 
303         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
304     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
305 }
306
307 /*!  \internal */
308 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope,
309                                                const QString &expression, QDeclarativeExpressionPrivate &dd)
310 : QObject(dd, 0)
311 {
312     Q_D(QDeclarativeExpression);
313     d->init(ctxt, expression, scope);
314
315     if (QDeclarativeExpression_notifyIdx == -1) 
316         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
317     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
318 }
319
320 /*!  
321     \internal 
322
323     To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.  
324     For example:
325         v8::Handle<v8::Function> function;
326         new QDeclarativeExpression(ctxt, scope, &function, ...);
327  */
328 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope, void *functionPtr,
329                                                QDeclarativeExpressionPrivate &dd)
330 : QObject(dd, 0)
331 {
332     v8::Handle<v8::Function> function = *(v8::Handle<v8::Function> *)functionPtr;
333
334     Q_D(QDeclarativeExpression);
335     d->init(ctxt, function, scope);
336
337     if (QDeclarativeExpression_notifyIdx == -1)
338         QDeclarativeExpression_notifyIdx = QDeclarativeExpression::staticMetaObject.indexOfMethod("_q_notify()");
339
340     d->setNotifyObject(this, QDeclarativeExpression_notifyIdx);
341 }
342
343 /*!
344     Destroy the QDeclarativeExpression instance.
345 */
346 QDeclarativeExpression::~QDeclarativeExpression()
347 {
348 }
349
350 /*!
351     Returns the QDeclarativeEngine this expression is associated with, or 0 if there
352     is no association or the QDeclarativeEngine has been destroyed.
353 */
354 QDeclarativeEngine *QDeclarativeExpression::engine() const
355 {
356     Q_D(const QDeclarativeExpression);
357     return d->context()?d->context()->engine:0;
358 }
359
360 /*!
361     Returns the QDeclarativeContext this expression is associated with, or 0 if there
362     is no association or the QDeclarativeContext has been destroyed.
363 */
364 QDeclarativeContext *QDeclarativeExpression::context() const
365 {
366     Q_D(const QDeclarativeExpression);
367     QDeclarativeContextData *data = d->context();
368     return data?data->asQDeclarativeContext():0;
369 }
370
371 /*!
372     Returns the expression string.
373 */
374 QString QDeclarativeExpression::expression() const
375 {
376     Q_D(const QDeclarativeExpression);
377     if (d->extractExpressionFromFunction && context()->engine()) {
378         QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(context()->engine());
379         v8::HandleScope handle_scope;
380         v8::Context::Scope scope(v8engine->context());
381
382         return v8engine->toString(v8::Handle<v8::Value>(d->v8function));
383     }
384     return d->expression;
385 }
386
387 /*!
388     Set the expression to \a expression.
389 */
390 void QDeclarativeExpression::setExpression(const QString &expression)
391 {
392     Q_D(QDeclarativeExpression);
393
394     d->resetNotifyOnValueChanged();
395     d->expression = expression;
396     d->expressionFunctionValid = false;
397     qPersistentDispose(d->v8function);
398     qPersistentDispose(d->v8qmlscope);
399 }
400
401 void QDeclarativeExpressionPrivate::exceptionToError(v8::Handle<v8::Message> message, 
402                                                      QDeclarativeError &error)
403 {
404     Q_ASSERT(!message.IsEmpty());
405
406     v8::Handle<v8::Value> name = message->GetScriptResourceName();
407     v8::Handle<v8::String> description = message->Get();
408     int lineNumber = message->GetLineNumber();
409
410     v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>();
411     if (file.IsEmpty() || file->Length() == 0) 
412         error.setUrl(QUrl(QLatin1String("<Unknown File>")));
413     else 
414         error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
415
416     error.setLine(lineNumber);
417     error.setColumn(-1);
418
419     QString qDescription = QV8Engine::toStringStatic(description);
420     if (qDescription.startsWith(QLatin1String("Uncaught ")))
421         qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
422
423     error.setDescription(qDescription);
424 }
425
426 void QDeclarativeJavaScriptExpression::setNotifyOnValueChanged(bool v)
427 {
428     m_notifyOnValueChanged = v;
429     if (!v) guardList.clear();
430 }
431
432 void QDeclarativeJavaScriptExpression::resetNotifyOnValueChanged()
433 {
434     guardList.clear();
435 }
436
437 void QDeclarativeJavaScriptExpression::setNotifyObject(QObject *object, int index)
438 {
439     guardList.clear();
440
441     m_notifyObject = object;
442     m_notifyIndex = index;
443
444     if (!object || index == -1) {
445         m_notifyObject = 0;
446         m_notifyIndex = -1;
447     }
448 }
449
450 v8::Local<v8::Value> QDeclarativeJavaScriptExpression::evaluate(v8::Handle<v8::Function> function, bool *isUndefined)
451 {
452     Q_ASSERT(context() && context()->engine);
453     Q_ASSERT(!notifyOnValueChanged() || (m_notifyObject && m_notifyIndex != -1));
454
455     if (function.IsEmpty() || function->IsUndefined()) {
456         if (isUndefined) *isUndefined = true;
457         return v8::Local<v8::Value>();
458     }
459
460     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine);
461
462     bool lastCaptureProperties = ep->captureProperties;
463     QPODVector<QDeclarativeEnginePrivate::CapturedProperty> lastCapturedProperties;
464     ep->captureProperties = notifyOnValueChanged();
465
466     if (ep->capturedProperties.count())
467         ep->capturedProperties.copyAndClear(lastCapturedProperties);
468
469     QDeclarativeContextData *lastSharedContext = 0;
470     QObject *lastSharedScope = 0;
471
472     bool sharedContext = useSharedContext();
473
474     // All code that follows must check with watcher before it accesses data members 
475     // incase we have been deleted.
476     QDeclarativeDeleteWatcher watcher(this);
477
478     if (sharedContext) {
479         lastSharedContext = ep->sharedContext;
480         lastSharedScope = ep->sharedScope;
481         ep->sharedContext = context();
482         ep->sharedScope = scopeObject();
483     }
484
485     v8::Local<v8::Value> result;
486     {
487         v8::TryCatch try_catch;
488         v8::Handle<v8::Object> This = ep->v8engine()->global();
489         if (scopeObject() && requiresThisObject()) {
490             v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject());
491             if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
492         }
493
494         result = function->Call(This, 0, 0);
495
496         if (isUndefined)
497             *isUndefined = try_catch.HasCaught() || result->IsUndefined();
498
499         if (watcher.wasDeleted()) {
500         } else if (try_catch.HasCaught()) {
501             v8::Context::Scope scope(ep->v8engine()->context());
502             v8::Local<v8::Message> message = try_catch.Message();
503             if (!message.IsEmpty()) {
504                 QDeclarativeExpressionPrivate::exceptionToError(message, error);
505             } else {
506                 error = QDeclarativeError();
507             }
508         } else {
509             error = QDeclarativeError();
510         }
511     }
512
513     if (sharedContext) {
514         ep->sharedContext = lastSharedContext;
515         ep->sharedScope = lastSharedScope;
516     }
517
518     if (!watcher.wasDeleted() && notifyOnValueChanged()) {
519         guardList.updateGuards(m_notifyObject, m_notifyIndex, this, ep->capturedProperties);
520     }
521
522     if (lastCapturedProperties.count())
523         lastCapturedProperties.copyAndClear(ep->capturedProperties);
524     else
525         ep->capturedProperties.clear();
526
527     ep->captureProperties = lastCaptureProperties;
528
529     return result;
530 }
531
532 void QDeclarativeJavaScriptExpression::GuardList::updateGuards(QObject *notifyObject, int notifyIndex,
533                                                                QDeclarativeJavaScriptExpression *expression,
534                                                                const CapturedProperties &properties)
535 {
536     Q_ASSERT(notifyObject);
537     Q_ASSERT(notifyIndex != -1);
538
539     if (properties.count() == 0) {
540         clear();
541         return;
542     }
543
544     if (properties.count() != length) {
545         QDeclarativeNotifierEndpoint *newGuardList = new QDeclarativeNotifierEndpoint[properties.count()];
546
547         for (int ii = 0; ii < qMin(length, properties.count()); ++ii) 
548            endpoints[ii].copyAndClear(newGuardList[ii]);
549
550         delete [] endpoints;
551         endpoints = newGuardList;
552         length = properties.count();
553     }
554
555     bool outputWarningHeader = false;
556     bool noChanges = true;
557     for (int ii = 0; ii < properties.count(); ++ii) {
558         QDeclarativeNotifierEndpoint &guard = endpoints[ii];
559         const QDeclarativeEnginePrivate::CapturedProperty &property = properties.at(ii);
560
561         guard.target = notifyObject;
562         guard.targetMethod = notifyIndex;
563
564         if (property.notifier != 0) {
565
566             if (!noChanges && guard.isConnected(property.notifier)) {
567                 // Nothing to do
568
569             } else {
570                 noChanges = false;
571
572                 bool existing = false;
573                 for (int jj = 0; !existing && jj < ii; ++jj) 
574                     if (endpoints[jj].isConnected(property.notifier)) 
575                         existing = true;
576
577                 if (existing) {
578                     // duplicate
579                     guard.disconnect();
580                 } else {
581                     guard.connect(property.notifier);
582                 }
583             }
584
585
586         } else if (property.notifyIndex != -1) {
587
588             if (!noChanges && guard.isConnected(property.object, property.notifyIndex)) {
589                 // Nothing to do
590
591             } else {
592                 noChanges = false;
593
594                 bool existing = false;
595                 for (int jj = 0; !existing && jj < ii; ++jj) 
596                     if (endpoints[jj].isConnected(property.object, property.notifyIndex)) 
597                         existing = true;
598
599                 if (existing) {
600                     // duplicate
601                     guard.disconnect();
602                 } else {
603                     guard.connect(property.object, property.notifyIndex);
604                 }
605             }
606
607         } else {
608             if (!outputWarningHeader) {
609                 QString e = expression->expressionIdentifier();
610                 outputWarningHeader = true;
611                 qWarning() << "QDeclarativeExpression: Expression" << qPrintable(e)
612                            << "depends on non-NOTIFYable properties:";
613             }
614
615             const QMetaObject *metaObj = property.object->metaObject();
616             QMetaProperty metaProp = metaObj->property(property.coreIndex);
617
618             qWarning().nospace() << "    " << metaObj->className() << "::" << metaProp.name();
619         }
620     }
621 }
622
623 // Must be called with a valid handle scope
624 v8::Local<v8::Value> QDeclarativeExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined)
625 {
626     if (!expressionFunctionValid) {
627         QDeclarativeRewrite::RewriteBinding rewriteBinding;
628         rewriteBinding.setName(name);
629         bool ok = true;
630         const QString code = rewriteBinding(expression, &ok);
631         if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
632         setUseSharedContext(false);
633         expressionFunctionValid = true;
634     }
635
636
637     if (secondaryScope) {
638         v8::Local<v8::Value> result;
639         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine);
640         QObject *restoreSecondaryScope = 0;
641         restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope);
642         result = evaluate(v8function, isUndefined);
643         ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope);
644         return result;
645     } else {
646         return evaluate(v8function, isUndefined);
647     }
648 }
649
650 QVariant QDeclarativeExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined)
651 {
652     Q_Q(QDeclarativeExpression);
653
654     if (!context() || !context()->isValid()) {
655         qWarning("QDeclarativeExpression: Attempted to evaluate an expression in an invalid context");
656         return QVariant();
657     }
658
659     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(q->engine());
660     QVariant rv;
661
662     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
663
664     {
665         v8::HandleScope handle_scope;
666         v8::Context::Scope context_scope(ep->v8engine()->context());
667         v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined);
668         rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
669     }
670
671     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
672
673     return rv;
674 }
675
676 /*!
677     Evaulates the expression, returning the result of the evaluation,
678     or an invalid QVariant if the expression is invalid or has an error.
679
680     \a valueIsUndefined is set to true if the expression resulted in an
681     undefined value.
682
683     \sa hasError(), error()
684 */
685 QVariant QDeclarativeExpression::evaluate(bool *valueIsUndefined)
686 {
687     Q_D(QDeclarativeExpression);
688     return d->value(0, valueIsUndefined);
689 }
690
691 /*!
692 Returns true if the valueChanged() signal is emitted when the expression's evaluated
693 value changes.
694 */
695 bool QDeclarativeExpression::notifyOnValueChanged() const
696 {
697     Q_D(const QDeclarativeExpression);
698     return d->notifyOnValueChanged();
699 }
700
701 /*!
702   Sets whether the valueChanged() signal is emitted when the
703   expression's evaluated value changes.
704
705   If \a notifyOnChange is true, the QDeclarativeExpression will
706   monitor properties involved in the expression's evaluation, and emit
707   QDeclarativeExpression::valueChanged() if they have changed.  This
708   allows an application to ensure that any value associated with the
709   result of the expression remains up to date.
710
711   If \a notifyOnChange is false (default), the QDeclarativeExpression
712   will not montitor properties involved in the expression's
713   evaluation, and QDeclarativeExpression::valueChanged() will never be
714   emitted.  This is more efficient if an application wants a "one off"
715   evaluation of the expression.
716 */
717 void QDeclarativeExpression::setNotifyOnValueChanged(bool notifyOnChange)
718 {
719     Q_D(QDeclarativeExpression);
720     d->setNotifyOnValueChanged(notifyOnChange);
721 }
722
723 /*!
724     Returns the source file URL for this expression.  The source location must
725     have been previously set by calling setSourceLocation().
726 */
727 QString QDeclarativeExpression::sourceFile() const
728 {
729     Q_D(const QDeclarativeExpression);
730     return d->url;
731 }
732
733 /*!
734     Returns the source file line number for this expression.  The source location 
735     must have been previously set by calling setSourceLocation().
736 */
737 int QDeclarativeExpression::lineNumber() const
738 {
739     Q_D(const QDeclarativeExpression);
740     return d->line;
741 }
742
743 /*!
744     Set the location of this expression to \a line of \a url. This information
745     is used by the script engine.
746 */
747 void QDeclarativeExpression::setSourceLocation(const QString &url, int line)
748 {
749     Q_D(QDeclarativeExpression);
750     d->url = url;
751     d->line = line;
752 }
753
754 /*!
755     Returns the expression's scope object, if provided, otherwise 0.
756
757     In addition to data provided by the expression's QDeclarativeContext, the scope
758     object's properties are also in scope during the expression's evaluation.
759 */
760 QObject *QDeclarativeExpression::scopeObject() const
761 {
762     Q_D(const QDeclarativeExpression);
763     return d->scopeObject();
764 }
765
766 /*!
767     Returns true if the last call to evaluate() resulted in an error,
768     otherwise false.
769     
770     \sa error(), clearError()
771 */
772 bool QDeclarativeExpression::hasError() const
773 {
774     Q_D(const QDeclarativeExpression);
775     return d->error.isValid();
776 }
777
778 /*!
779     Clear any expression errors.  Calls to hasError() following this will
780     return false.
781
782     \sa hasError(), error()
783 */
784 void QDeclarativeExpression::clearError()
785 {
786     Q_D(QDeclarativeExpression);
787     d->error = QDeclarativeError();
788 }
789
790 /*!
791     Return any error from the last call to evaluate().  If there was no error,
792     this returns an invalid QDeclarativeError instance.
793
794     \sa hasError(), clearError()
795 */
796
797 QDeclarativeError QDeclarativeExpression::error() const
798 {
799     Q_D(const QDeclarativeExpression);
800     return d->error;
801 }
802
803 /*! \internal */
804 void QDeclarativeExpressionPrivate::_q_notify()
805 {
806     emitValueChanged();
807 }
808
809 /*!
810     \fn void QDeclarativeExpression::valueChanged()
811
812     Emitted each time the expression value changes from the last time it was
813     evaluated.  The expression must have been evaluated at least once (by
814     calling QDeclarativeExpression::evaluate()) before this signal will be emitted.
815 */
816
817 void QDeclarativeExpressionPrivate::emitValueChanged()
818 {
819     Q_Q(QDeclarativeExpression);
820     emit q->valueChanged();
821 }
822
823 QDeclarativeAbstractExpression::QDeclarativeAbstractExpression()
824 : m_context(0), m_prevExpression(0), m_nextExpression(0)
825 {
826 }
827
828 QDeclarativeAbstractExpression::~QDeclarativeAbstractExpression()
829 {
830     if (m_prevExpression) {
831         *m_prevExpression = m_nextExpression;
832         if (m_nextExpression) 
833             m_nextExpression->m_prevExpression = m_prevExpression;
834     }
835 }
836
837 QDeclarativeContextData *QDeclarativeAbstractExpression::context() const
838 {
839     return m_context;
840 }
841
842 void QDeclarativeAbstractExpression::setContext(QDeclarativeContextData *context)
843 {
844     if (m_prevExpression) {
845         *m_prevExpression = m_nextExpression;
846         if (m_nextExpression) 
847             m_nextExpression->m_prevExpression = m_prevExpression;
848         m_prevExpression = 0;
849         m_nextExpression = 0;
850     }
851
852     m_context = context;
853
854     if (m_context) {
855         m_nextExpression = m_context->expressions;
856         if (m_nextExpression) 
857             m_nextExpression->m_prevExpression = &m_nextExpression;
858         m_prevExpression = &context->expressions;
859         m_context->expressions = this;
860     }
861 }
862
863 void QDeclarativeAbstractExpression::refresh()
864 {
865 }
866
867 bool QDeclarativeAbstractExpression::isValid() const
868 {
869     return m_context != 0;
870 }
871
872 QT_END_NAMESPACE
873
874 #include <moc_qdeclarativeexpression.cpp>