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);
232 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
237 errorString = new QStringList;
238 QString preamble = QLatin1String("QQmlExpression: Expression ") +
239 expression->m_vtable->expressionIdentifier(expression) +
240 QLatin1String(" depends on non-NOTIFYable properties:");
241 errorString->append(preamble);
244 const QMetaObject *metaObj = o->metaObject();
245 QMetaProperty metaProp = metaObj->property(c);
247 QString error = QLatin1String(" ") +
248 QString::fromUtf8(metaObj->className()) +
249 QLatin1String("::") +
250 QString::fromUtf8(metaProp.name());
251 errorString->append(error);
254 // Try and find a matching guard
255 while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
256 guards.takeFirst()->Delete();
259 if (!guards.isEmpty()) {
260 g = guards.takeFirst();
262 Q_ASSERT(g->isConnected(o, n));
264 g = Guard::New(expression, engine);
265 g->connect(o, n, engine);
268 expression->activeGuards.prepend(g);
273 void QQmlJavaScriptExpression::clearError()
275 if (m_vtable.hasValue()) {
276 m_vtable.value().clearError();
277 m_vtable.value().removeError();
281 QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
283 if (m_vtable.hasValue()) return m_vtable.constValue()->error(engine);
284 else return QQmlError();
287 QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
289 return &m_vtable.value();
292 void QQmlJavaScriptExpression::exceptionToError(v8::Handle<v8::Message> message, QQmlError &error)
294 Q_ASSERT(!message.IsEmpty());
296 v8::Handle<v8::Value> name = message->GetScriptResourceName();
297 v8::Handle<v8::String> description = message->Get();
298 int lineNumber = message->GetLineNumber();
300 v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>();
301 if (file.IsEmpty() || file->Length() == 0)
302 error.setUrl(QUrl());
304 error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
306 error.setLine(lineNumber);
309 QString qDescription = QV8Engine::toStringStatic(description);
310 if (qDescription.startsWith(QLatin1String("Uncaught ")))
311 qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
313 error.setDescription(qDescription);
316 // Callee owns the persistent handle
317 v8::Persistent<v8::Function>
318 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
319 const char *code, int codeLength,
320 const QString &filename, int line,
321 v8::Persistent<v8::Object> *qmlscope)
323 QQmlEngine *engine = ctxt->engine;
324 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
326 v8::HandleScope handle_scope;
327 v8::Context::Scope ctxtscope(ep->v8engine()->context());
330 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
331 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, codeLength, filename, line);
332 if (tc.HasCaught()) {
334 error.setDescription(QLatin1String("Exception occurred during function compilation"));
336 error.setUrl(QUrl::fromLocalFile(filename));
337 v8::Local<v8::Message> message = tc.Message();
338 if (!message.IsEmpty())
339 QQmlExpressionPrivate::exceptionToError(message, error);
341 return v8::Persistent<v8::Function>();
343 v8::Local<v8::Value> result = script->Run(scopeobject);
344 if (tc.HasCaught()) {
346 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
348 error.setUrl(QUrl::fromLocalFile(filename));
349 v8::Local<v8::Message> message = tc.Message();
350 if (!message.IsEmpty())
351 QQmlExpressionPrivate::exceptionToError(message, error);
353 return v8::Persistent<v8::Function>();
355 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
356 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
359 // Callee owns the persistent handle
360 v8::Persistent<v8::Function>
361 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
362 const QString &code, const QString &filename, int line,
363 v8::Persistent<v8::Object> *qmlscope)
365 QQmlEngine *engine = ctxt->engine;
366 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
368 v8::HandleScope handle_scope;
369 v8::Context::Scope ctxtscope(ep->v8engine()->context());
372 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
373 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line);
374 if (tc.HasCaught()) {
376 error.setDescription(QLatin1String("Exception occurred during function compilation"));
378 error.setUrl(QUrl::fromLocalFile(filename));
379 v8::Local<v8::Message> message = tc.Message();
380 if (!message.IsEmpty())
381 QQmlExpressionPrivate::exceptionToError(message, error);
383 return v8::Persistent<v8::Function>();
385 v8::Local<v8::Value> result = script->Run(scopeobject);
386 if (tc.HasCaught()) {
388 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
390 error.setUrl(QUrl::fromLocalFile(filename));
391 v8::Local<v8::Message> message = tc.Message();
392 if (!message.IsEmpty())
393 QQmlExpressionPrivate::exceptionToError(message, error);
395 return v8::Persistent<v8::Function>();
397 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
398 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
401 void QQmlJavaScriptExpression::clearGuards()
403 while (Guard *g = activeGuards.takeFirst())
407 void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
409 QQmlJavaScriptExpression *expression =
410 static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression;
412 expression->m_vtable->expressionChanged(expression);