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 QtQml 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 "qqmljavascriptexpression_p.h"
44 #include <private/qqmlexpression_p.h>
48 bool QQmlDelayedError::addError(QQmlEnginePrivate *e)
52 if (e->inProgressCreations == 0) return false; // Not in construction
54 if (prevError) return true; // Already in error chain
56 prevError = &e->erroredBindings;
57 nextError = e->erroredBindings;
58 e->erroredBindings = this;
59 if (nextError) nextError->prevError = &nextError;
64 void QQmlDelayedError::setMessage(v8::Handle<v8::Message> message)
66 qPersistentDispose(m_message);
67 m_message = qPersistentNew<v8::Message>(message);
70 void QQmlDelayedError::setErrorLocation(const QUrl &url, int line, int column)
73 m_error.setLine(line);
74 m_error.setColumn(column);
77 void QQmlDelayedError::setErrorDescription(const QString &description)
79 m_error.setDescription(description);
83 Converting from a message to an error is relatively expensive.
85 We don't want to do this work for transient exceptions (exceptions
86 that occur during startup because of the order of binding
87 execution, but have gone away by the time startup has finished), so we
88 delay conversion until it is required for displaying the error.
90 void QQmlDelayedError::convertMessageToError(QQmlEngine *engine) const
92 if (!m_message.IsEmpty() && engine) {
93 v8::HandleScope handle_scope;
94 v8::Context::Scope context_scope(QQmlEnginePrivate::getV8Engine(engine)->context());
95 QQmlExpressionPrivate::exceptionToError(m_message, m_error);
96 qPersistentDispose(m_message);
100 QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v)
105 QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
108 if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
109 m_scopeObject.asT2()->_s = 0;
112 void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
114 activeGuards.setFlagValue(v);
115 if (!v) clearGuards();
118 void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
124 QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
125 v8::Handle<v8::Function> function, bool *isUndefined)
127 Q_ASSERT(context && context->engine);
129 if (function.IsEmpty() || function->IsUndefined()) {
130 if (isUndefined) *isUndefined = true;
131 return v8::Local<v8::Value>();
134 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
136 Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
137 GuardCapture capture(context->engine, this);
139 QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
140 ep->propertyCapture = notifyOnValueChanged()?&capture:0;
143 if (notifyOnValueChanged())
144 capture.guards.copyAndClearPrepend(activeGuards);
146 QQmlContextData *lastSharedContext = 0;
147 QObject *lastSharedScope = 0;
149 bool sharedContext = useSharedContext();
151 // All code that follows must check with watcher before it accesses data members
152 // incase we have been deleted.
153 DeleteWatcher watcher(this);
156 lastSharedContext = ep->sharedContext;
157 lastSharedScope = ep->sharedScope;
158 ep->sharedContext = context;
159 ep->sharedScope = scopeObject();
162 v8::Local<v8::Value> result;
164 v8::TryCatch try_catch;
165 v8::Handle<v8::Object> This = ep->v8engine()->global();
166 if (scopeObject() && requiresThisObject()) {
167 v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject());
168 if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
171 result = function->Call(This, 0, 0);
174 *isUndefined = try_catch.HasCaught() || result->IsUndefined();
176 if (watcher.wasDeleted()) {
177 } else if (try_catch.HasCaught()) {
178 v8::Context::Scope scope(ep->v8engine()->context());
179 v8::Local<v8::Message> message = try_catch.Message();
180 if (!message.IsEmpty()) {
181 delayedError()->setMessage(message);
183 if (hasDelayedError()) delayedError()->clearError();
186 if (hasDelayedError()) delayedError()->clearError();
191 ep->sharedContext = lastSharedContext;
192 ep->sharedScope = lastSharedScope;
195 if (capture.errorString) {
196 for (int ii = 0; ii < capture.errorString->count(); ++ii)
197 qWarning("%s", qPrintable(capture.errorString->at(ii)));
198 delete capture.errorString;
199 capture.errorString = 0;
202 while (Guard *g = capture.guards.takeFirst())
205 ep->propertyCapture = lastPropertyCapture;
210 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n)
214 // Try and find a matching guard
215 while (!guards.isEmpty() && !guards.first()->isConnected(n))
216 guards.takeFirst()->Delete();
219 if (!guards.isEmpty()) {
220 g = guards.takeFirst();
222 Q_ASSERT(g->isConnected(n));
224 g = Guard::New(expression, engine);
228 expression->activeGuards.prepend(g);
235 \a n is in the signal index range (see QObjectPrivate::signalIndex()).
237 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
242 errorString = new QStringList;
243 QString preamble = QLatin1String("QQmlExpression: Expression ") +
244 expression->m_vtable->expressionIdentifier(expression) +
245 QLatin1String(" depends on non-NOTIFYable properties:");
246 errorString->append(preamble);
249 const QMetaObject *metaObj = o->metaObject();
250 QMetaProperty metaProp = metaObj->property(c);
252 QString error = QLatin1String(" ") +
253 QString::fromUtf8(metaObj->className()) +
254 QLatin1String("::") +
255 QString::fromUtf8(metaProp.name());
256 errorString->append(error);
259 // Try and find a matching guard
260 while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
261 guards.takeFirst()->Delete();
264 if (!guards.isEmpty()) {
265 g = guards.takeFirst();
267 Q_ASSERT(g->isConnected(o, n));
269 g = Guard::New(expression, engine);
270 g->connect(o, n, engine);
273 expression->activeGuards.prepend(g);
278 void QQmlJavaScriptExpression::clearError()
280 if (m_vtable.hasValue()) {
281 m_vtable.value().clearError();
282 m_vtable.value().removeError();
286 QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
288 if (m_vtable.hasValue()) return m_vtable.constValue()->error(engine);
289 else return QQmlError();
292 QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
294 return &m_vtable.value();
297 void QQmlJavaScriptExpression::exceptionToError(v8::Handle<v8::Message> message, QQmlError &error)
299 Q_ASSERT(!message.IsEmpty());
301 v8::Handle<v8::Value> name = message->GetScriptResourceName();
302 v8::Handle<v8::String> description = message->Get();
303 int lineNumber = message->GetLineNumber();
305 v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>();
306 if (file.IsEmpty() || file->Length() == 0)
307 error.setUrl(QUrl());
309 error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
311 error.setLine(lineNumber);
314 QString qDescription = QV8Engine::toStringStatic(description);
315 if (qDescription.startsWith(QLatin1String("Uncaught ")))
316 qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
318 error.setDescription(qDescription);
321 // Callee owns the persistent handle
322 v8::Persistent<v8::Function>
323 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
324 const char *code, int codeLength,
325 const QString &filename, int line,
326 v8::Persistent<v8::Object> *qmlscope)
328 QQmlEngine *engine = ctxt->engine;
329 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
331 v8::HandleScope handle_scope;
332 v8::Context::Scope ctxtscope(ep->v8engine()->context());
335 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
336 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, codeLength, filename, line);
337 if (tc.HasCaught()) {
339 error.setDescription(QLatin1String("Exception occurred during function compilation"));
341 error.setUrl(QUrl::fromLocalFile(filename));
342 v8::Local<v8::Message> message = tc.Message();
343 if (!message.IsEmpty())
344 QQmlExpressionPrivate::exceptionToError(message, error);
346 return v8::Persistent<v8::Function>();
348 v8::Local<v8::Value> result = script->Run(scopeobject);
349 if (tc.HasCaught()) {
351 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
353 error.setUrl(QUrl::fromLocalFile(filename));
354 v8::Local<v8::Message> message = tc.Message();
355 if (!message.IsEmpty())
356 QQmlExpressionPrivate::exceptionToError(message, error);
358 return v8::Persistent<v8::Function>();
360 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
361 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
364 // Callee owns the persistent handle
365 v8::Persistent<v8::Function>
366 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
367 const QString &code, const QString &filename, int line,
368 v8::Persistent<v8::Object> *qmlscope)
370 QQmlEngine *engine = ctxt->engine;
371 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
373 v8::HandleScope handle_scope;
374 v8::Context::Scope ctxtscope(ep->v8engine()->context());
377 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
378 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line);
379 if (tc.HasCaught()) {
381 error.setDescription(QLatin1String("Exception occurred during function compilation"));
383 error.setUrl(QUrl::fromLocalFile(filename));
384 v8::Local<v8::Message> message = tc.Message();
385 if (!message.IsEmpty())
386 QQmlExpressionPrivate::exceptionToError(message, error);
388 return v8::Persistent<v8::Function>();
390 v8::Local<v8::Value> result = script->Run(scopeobject);
391 if (tc.HasCaught()) {
393 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
395 error.setUrl(QUrl::fromLocalFile(filename));
396 v8::Local<v8::Message> message = tc.Message();
397 if (!message.IsEmpty())
398 QQmlExpressionPrivate::exceptionToError(message, error);
400 return v8::Persistent<v8::Function>();
402 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
403 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
406 void QQmlJavaScriptExpression::clearGuards()
408 while (Guard *g = activeGuards.takeFirst())
412 void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
414 QQmlJavaScriptExpression *expression =
415 static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression;
417 expression->m_vtable->expressionChanged(expression);