1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativeexpression.h"
43 #include "qdeclarativeexpression_p.h"
45 #include "qdeclarativeengine_p.h"
46 #include "qdeclarativecontext_p.h"
47 #include "qdeclarativerewrite_p.h"
48 #include "qdeclarativescriptstring_p.h"
49 #include "qdeclarativecompiler_p.h"
51 #include <QtCore/qdebug.h>
55 bool QDeclarativeDelayedError::addError(QDeclarativeEnginePrivate *e)
59 if (e->inProgressCreations == 0) return false; // Not in construction
61 if (prevError) return true; // Already in error chain
63 prevError = &e->erroredBindings;
64 nextError = e->erroredBindings;
65 e->erroredBindings = this;
66 if (nextError) nextError->prevError = &nextError;
71 QDeclarativeJavaScriptExpression::QDeclarativeJavaScriptExpression()
72 : m_requiresThisObject(0), m_useSharedContext(0), m_notifyOnValueChanged(0),
73 m_scopeObject(0), guardCapture(0)
77 QDeclarativeJavaScriptExpression::~QDeclarativeJavaScriptExpression()
79 if (guardCapture) guardCapture->expression = 0;
83 QDeclarativeExpressionPrivate::QDeclarativeExpressionPrivate()
84 : expressionFunctionValid(true), expressionFunctionRewritten(false),
85 extractExpressionFromFunction(false), line(-1), dataRef(0)
89 QDeclarativeExpressionPrivate::~QDeclarativeExpressionPrivate()
91 qPersistentDispose(v8qmlscope);
92 qPersistentDispose(v8function);
93 if (dataRef) dataRef->release();
97 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QString &expr,
102 QDeclarativeAbstractExpression::setContext(ctxt);
104 expressionFunctionValid = false;
105 expressionFunctionRewritten = false;
108 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, v8::Handle<v8::Function> func,
111 QDeclarativeAbstractExpression::setContext(ctxt);
114 v8function = qPersistentNew<v8::Function>(func);
115 setUseSharedContext(false);
116 expressionFunctionValid = true;
117 expressionFunctionRewritten = false;
118 extractExpressionFromFunction = true;
121 void QDeclarativeExpressionPrivate::init(QDeclarativeContextData *ctxt, const QString &expr,
122 bool isRewritten, QObject *me, const QString &srcUrl,
123 int lineNumber, int columnNumber)
127 column = columnNumber;
131 expressionFunctionValid = false;
132 expressionFunctionRewritten = isRewritten;
134 QDeclarativeAbstractExpression::setContext(ctxt);
138 // Callee owns the persistent handle
139 v8::Persistent<v8::Function>
140 QDeclarativeExpressionPrivate::evalFunction(QDeclarativeContextData *ctxt, QObject *scope,
141 const QString &code, const QString &filename, int line,
142 v8::Persistent<v8::Object> *qmlscope)
144 QDeclarativeEngine *engine = ctxt->engine;
145 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
147 // XXX TODO: Implement script caching, like we used to do with QScriptProgram in the
149 v8::HandleScope handle_scope;
150 v8::Context::Scope ctxtscope(ep->v8engine()->context());
153 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
154 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line);
155 if (tc.HasCaught()) return v8::Persistent<v8::Function>();
156 v8::Local<v8::Value> result = script->Run(scopeobject);
157 if (tc.HasCaught()) return v8::Persistent<v8::Function>();
158 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
159 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
162 QDeclarativeExpression *QDeclarativeExpressionPrivate::create(QDeclarativeContextData *ctxt, QObject *object, const QString &expr, bool isRewritten,
163 const QString &url, int lineNumber, int columnNumber)
165 return new QDeclarativeExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber, *new QDeclarativeExpressionPrivate);
169 \class QDeclarativeExpression
171 \brief The QDeclarativeExpression class evaluates JavaScript in a QML context.
173 For example, given a file \c main.qml like this:
179 width: 200; height: 200
183 The following code evaluates a JavaScript expression in the context of the
187 QDeclarativeEngine *engine = new QDeclarativeEngine;
188 QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml"));
190 QObject *myObject = component.create();
191 QDeclarativeExpression *expr = new QDeclarativeExpression(engine->rootContext(), myObject, "width * 2");
192 int result = expr->evaluate().toInt(); // result = 400
197 Create an invalid QDeclarativeExpression.
199 As the expression will not have an associated QDeclarativeContext, this will be a
200 null expression object and its value will always be an invalid QVariant.
202 QDeclarativeExpression::QDeclarativeExpression()
203 : QObject(*new QDeclarativeExpressionPrivate, 0)
208 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt,
209 QObject *object, const QString &expr, bool isRewritten,
210 const QString &url, int lineNumber, int columnNumber,
211 QDeclarativeExpressionPrivate &dd)
214 Q_D(QDeclarativeExpression);
215 d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
219 Create a QDeclarativeExpression object that is a child of \a parent.
221 The \script provides the expression to be evaluated, the context to evaluate it in,
222 and the scope object to evaluate it with.
224 This constructor is functionally equivalent to the following, but in most cases
227 QDeclarativeExpression expression(script.context(), script.scopeObject(), script.script(), parent);
230 \sa QDeclarativeScriptString
232 QDeclarativeExpression::QDeclarativeExpression(const QDeclarativeScriptString &script, QObject *parent)
233 : QObject(*new QDeclarativeExpressionPrivate, parent)
235 Q_D(QDeclarativeExpression);
236 bool defaultConstruction = false;
238 int id = script.d.data()->bindingId;
240 defaultConstruction = true;
242 QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(script.context());
244 QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(script.context()->engine());
245 QDeclarativeCompiledData *cdata = 0;
246 QDeclarativeTypeData *typeData = 0;
247 if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
248 typeData = engine->typeLoader.get(ctxtdata->url);
249 cdata = typeData->compiledData();
253 d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
254 cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
256 defaultConstruction = true;
264 if (defaultConstruction)
265 d->init(QDeclarativeContextData::get(script.context()), script.script(), script.scopeObject());
269 Create a QDeclarativeExpression object that is a child of \a parent.
271 The \a expression JavaScript will be executed in the \a ctxt QDeclarativeContext.
272 If specified, the \a scope object's properties will also be in scope during
273 the expression's execution.
275 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContext *ctxt,
277 const QString &expression,
279 : QObject(*new QDeclarativeExpressionPrivate, parent)
281 Q_D(QDeclarativeExpression);
282 d->init(QDeclarativeContextData::get(ctxt), expression, scope);
288 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope,
289 const QString &expression)
290 : QObject(*new QDeclarativeExpressionPrivate, 0)
292 Q_D(QDeclarativeExpression);
293 d->init(ctxt, expression, scope);
297 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope,
298 const QString &expression, QDeclarativeExpressionPrivate &dd)
301 Q_D(QDeclarativeExpression);
302 d->init(ctxt, expression, scope);
308 To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.
310 v8::Handle<v8::Function> function;
311 new QDeclarativeExpression(ctxt, scope, &function, ...);
313 QDeclarativeExpression::QDeclarativeExpression(QDeclarativeContextData *ctxt, QObject *scope, void *functionPtr,
314 QDeclarativeExpressionPrivate &dd)
317 v8::Handle<v8::Function> function = *(v8::Handle<v8::Function> *)functionPtr;
319 Q_D(QDeclarativeExpression);
320 d->init(ctxt, function, scope);
324 Destroy the QDeclarativeExpression instance.
326 QDeclarativeExpression::~QDeclarativeExpression()
331 Returns the QDeclarativeEngine this expression is associated with, or 0 if there
332 is no association or the QDeclarativeEngine has been destroyed.
334 QDeclarativeEngine *QDeclarativeExpression::engine() const
336 Q_D(const QDeclarativeExpression);
337 return d->context()?d->context()->engine:0;
341 Returns the QDeclarativeContext this expression is associated with, or 0 if there
342 is no association or the QDeclarativeContext has been destroyed.
344 QDeclarativeContext *QDeclarativeExpression::context() const
346 Q_D(const QDeclarativeExpression);
347 QDeclarativeContextData *data = d->context();
348 return data?data->asQDeclarativeContext():0;
352 Returns the expression string.
354 QString QDeclarativeExpression::expression() const
356 Q_D(const QDeclarativeExpression);
357 if (d->extractExpressionFromFunction && context()->engine()) {
358 QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(context()->engine());
359 v8::HandleScope handle_scope;
360 v8::Context::Scope scope(v8engine->context());
362 return v8engine->toString(v8::Handle<v8::Value>(d->v8function));
364 return d->expression;
368 Set the expression to \a expression.
370 void QDeclarativeExpression::setExpression(const QString &expression)
372 Q_D(QDeclarativeExpression);
374 d->resetNotifyOnValueChanged();
375 d->expression = expression;
376 d->expressionFunctionValid = false;
377 d->expressionFunctionRewritten = false;
378 qPersistentDispose(d->v8function);
379 qPersistentDispose(d->v8qmlscope);
382 void QDeclarativeExpressionPrivate::exceptionToError(v8::Handle<v8::Message> message,
383 QDeclarativeError &error)
385 Q_ASSERT(!message.IsEmpty());
387 v8::Handle<v8::Value> name = message->GetScriptResourceName();
388 v8::Handle<v8::String> description = message->Get();
389 int lineNumber = message->GetLineNumber();
391 v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>();
392 if (file.IsEmpty() || file->Length() == 0)
393 error.setUrl(QUrl(QLatin1String("<Unknown File>")));
395 error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
397 error.setLine(lineNumber);
400 QString qDescription = QV8Engine::toStringStatic(description);
401 if (qDescription.startsWith(QLatin1String("Uncaught ")))
402 qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
404 error.setDescription(qDescription);
407 void QDeclarativeJavaScriptExpression::setNotifyOnValueChanged(bool v)
409 m_notifyOnValueChanged = v;
410 if (!v) clearGuards();
413 void QDeclarativeJavaScriptExpression::resetNotifyOnValueChanged()
418 v8::Local<v8::Value> QDeclarativeJavaScriptExpression::evaluate(v8::Handle<v8::Function> function, bool *isUndefined)
420 Q_ASSERT(context() && context()->engine);
422 if (function.IsEmpty() || function->IsUndefined()) {
423 if (isUndefined) *isUndefined = true;
424 return v8::Local<v8::Value>();
427 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine);
429 Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
430 GuardCapture capture(this);
432 QDeclarativeEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
433 ep->propertyCapture = notifyOnValueChanged()?&capture:0;
436 if (notifyOnValueChanged())
437 capture.guards.copyAndClear(activeGuards);
439 QDeclarativeContextData *lastSharedContext = 0;
440 QObject *lastSharedScope = 0;
442 bool sharedContext = useSharedContext();
444 // All code that follows must check with watcher before it accesses data members
445 // incase we have been deleted.
446 QDeleteWatcher watcher(this);
449 lastSharedContext = ep->sharedContext;
450 lastSharedScope = ep->sharedScope;
451 ep->sharedContext = context();
452 ep->sharedScope = scopeObject();
455 v8::Local<v8::Value> result;
457 v8::TryCatch try_catch;
458 v8::Handle<v8::Object> This = ep->v8engine()->global();
459 if (scopeObject() && requiresThisObject()) {
460 v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject());
461 if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
464 result = function->Call(This, 0, 0);
467 *isUndefined = try_catch.HasCaught() || result->IsUndefined();
469 if (watcher.wasDeleted()) {
470 } else if (try_catch.HasCaught()) {
471 v8::Context::Scope scope(ep->v8engine()->context());
472 v8::Local<v8::Message> message = try_catch.Message();
473 if (!message.IsEmpty()) {
474 QDeclarativeExpressionPrivate::exceptionToError(message, error);
476 error = QDeclarativeError();
479 error = QDeclarativeError();
484 ep->sharedContext = lastSharedContext;
485 ep->sharedScope = lastSharedScope;
488 if (capture.errorString) {
489 for (int ii = 0; ii < capture.errorString->count(); ++ii)
490 qWarning("%s", qPrintable(capture.errorString->at(ii)));
491 delete capture.errorString;
492 capture.errorString = 0;
495 while (Guard *g = capture.guards.takeFirst())
498 ep->propertyCapture = lastPropertyCapture;
503 void QDeclarativeJavaScriptExpression::GuardCapture::captureProperty(QDeclarativeNotifier *n)
507 // Try and find a matching guard
508 while (!guards.isEmpty() && !guards.first()->isConnected(n))
509 guards.takeFirst()->Delete();
512 if (!guards.isEmpty()) {
513 g = guards.takeFirst();
515 Q_ASSERT(g->isConnected(n));
517 g = Guard::New(expression);
521 expression->activeGuards.append(g);
525 void QDeclarativeJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
530 errorString = new QStringList;
531 QString preamble = QLatin1String("QDeclarativeExpression: Expression ") +
532 expression->expressionIdentifier() +
533 QLatin1String(" depends on non-NOTIFYable properties:");
534 errorString->append(preamble);
537 const QMetaObject *metaObj = o->metaObject();
538 QMetaProperty metaProp = metaObj->property(c);
540 QString error = QLatin1String(" ") +
541 QString::fromUtf8(metaObj->className()) +
542 QLatin1String("::") +
543 QString::fromUtf8(metaProp.name());
544 errorString->append(error);
547 // Try and find a matching guard
548 while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
549 guards.takeFirst()->Delete();
552 if (!guards.isEmpty()) {
553 g = guards.takeFirst();
555 Q_ASSERT(g->isConnected(o, n));
557 g = Guard::New(expression);
561 expression->activeGuards.append(g);
566 void QDeclarativeJavaScriptExpression::clearGuards()
568 while (Guard *g = activeGuards.takeFirst())
572 // Must be called with a valid handle scope
573 v8::Local<v8::Value> QDeclarativeExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined)
575 if (!expressionFunctionValid) {
578 QDeclarativeRewrite::RewriteBinding rewriteBinding;
579 rewriteBinding.setName(name);
581 if (expressionFunctionRewritten)
584 code = rewriteBinding(expression, &ok);
586 if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
587 setUseSharedContext(false);
588 expressionFunctionValid = true;
592 if (secondaryScope) {
593 v8::Local<v8::Value> result;
594 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine);
595 QObject *restoreSecondaryScope = 0;
596 restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope);
597 result = evaluate(v8function, isUndefined);
598 ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope);
601 return evaluate(v8function, isUndefined);
605 QVariant QDeclarativeExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined)
607 Q_Q(QDeclarativeExpression);
609 if (!context() || !context()->isValid()) {
610 qWarning("QDeclarativeExpression: Attempted to evaluate an expression in an invalid context");
614 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(q->engine());
617 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
620 v8::HandleScope handle_scope;
621 v8::Context::Scope context_scope(ep->v8engine()->context());
622 v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined);
623 rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
626 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
632 Evaulates the expression, returning the result of the evaluation,
633 or an invalid QVariant if the expression is invalid or has an error.
635 \a valueIsUndefined is set to true if the expression resulted in an
638 \sa hasError(), error()
640 QVariant QDeclarativeExpression::evaluate(bool *valueIsUndefined)
642 Q_D(QDeclarativeExpression);
643 return d->value(0, valueIsUndefined);
647 Returns true if the valueChanged() signal is emitted when the expression's evaluated
650 bool QDeclarativeExpression::notifyOnValueChanged() const
652 Q_D(const QDeclarativeExpression);
653 return d->notifyOnValueChanged();
657 Sets whether the valueChanged() signal is emitted when the
658 expression's evaluated value changes.
660 If \a notifyOnChange is true, the QDeclarativeExpression will
661 monitor properties involved in the expression's evaluation, and emit
662 QDeclarativeExpression::valueChanged() if they have changed. This
663 allows an application to ensure that any value associated with the
664 result of the expression remains up to date.
666 If \a notifyOnChange is false (default), the QDeclarativeExpression
667 will not montitor properties involved in the expression's
668 evaluation, and QDeclarativeExpression::valueChanged() will never be
669 emitted. This is more efficient if an application wants a "one off"
670 evaluation of the expression.
672 void QDeclarativeExpression::setNotifyOnValueChanged(bool notifyOnChange)
674 Q_D(QDeclarativeExpression);
675 d->setNotifyOnValueChanged(notifyOnChange);
679 Returns the source file URL for this expression. The source location must
680 have been previously set by calling setSourceLocation().
682 QString QDeclarativeExpression::sourceFile() const
684 Q_D(const QDeclarativeExpression);
689 Returns the source file line number for this expression. The source location
690 must have been previously set by calling setSourceLocation().
692 int QDeclarativeExpression::lineNumber() const
694 Q_D(const QDeclarativeExpression);
699 Returns the source file column number for this expression. The source location
700 must have been previously set by calling setSourceLocation().
702 int QDeclarativeExpression::columnNumber() const
704 Q_D(const QDeclarativeExpression);
709 Set the location of this expression to \a line of \a url. This information
710 is used by the script engine.
712 void QDeclarativeExpression::setSourceLocation(const QString &url, int line, int column)
714 Q_D(QDeclarativeExpression);
721 Returns the expression's scope object, if provided, otherwise 0.
723 In addition to data provided by the expression's QDeclarativeContext, the scope
724 object's properties are also in scope during the expression's evaluation.
726 QObject *QDeclarativeExpression::scopeObject() const
728 Q_D(const QDeclarativeExpression);
729 return d->scopeObject();
733 Returns true if the last call to evaluate() resulted in an error,
736 \sa error(), clearError()
738 bool QDeclarativeExpression::hasError() const
740 Q_D(const QDeclarativeExpression);
741 return d->error.isValid();
745 Clear any expression errors. Calls to hasError() following this will
748 \sa hasError(), error()
750 void QDeclarativeExpression::clearError()
752 Q_D(QDeclarativeExpression);
753 d->error = QDeclarativeError();
757 Return any error from the last call to evaluate(). If there was no error,
758 this returns an invalid QDeclarativeError instance.
760 \sa hasError(), clearError()
763 QDeclarativeError QDeclarativeExpression::error() const
765 Q_D(const QDeclarativeExpression);
770 \fn void QDeclarativeExpression::valueChanged()
772 Emitted each time the expression value changes from the last time it was
773 evaluated. The expression must have been evaluated at least once (by
774 calling QDeclarativeExpression::evaluate()) before this signal will be emitted.
777 void QDeclarativeExpressionPrivate::expressionChanged()
779 Q_Q(QDeclarativeExpression);
780 emit q->valueChanged();
783 QDeclarativeAbstractExpression::QDeclarativeAbstractExpression()
784 : m_context(0), m_prevExpression(0), m_nextExpression(0)
788 QDeclarativeAbstractExpression::~QDeclarativeAbstractExpression()
790 if (m_prevExpression) {
791 *m_prevExpression = m_nextExpression;
792 if (m_nextExpression)
793 m_nextExpression->m_prevExpression = m_prevExpression;
797 QDeclarativeContextData *QDeclarativeAbstractExpression::context() const
802 void QDeclarativeAbstractExpression::setContext(QDeclarativeContextData *context)
804 if (m_prevExpression) {
805 *m_prevExpression = m_nextExpression;
806 if (m_nextExpression)
807 m_nextExpression->m_prevExpression = m_prevExpression;
808 m_prevExpression = 0;
809 m_nextExpression = 0;
815 m_nextExpression = m_context->expressions;
816 if (m_nextExpression)
817 m_nextExpression->m_prevExpression = &m_nextExpression;
818 m_prevExpression = &context->expressions;
819 m_context->expressions = this;
823 void QDeclarativeAbstractExpression::refresh()
827 bool QDeclarativeAbstractExpression::isValid() const
829 return m_context != 0;
834 #include <moc_qdeclarativeexpression.cpp>