1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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, quint16 line, quint16 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 return evaluate(context, function, 0, 0, isUndefined);
131 QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
132 v8::Handle<v8::Function> function,
133 int argc, v8::Handle<v8::Value> args[],
136 Q_ASSERT(context && context->engine);
138 if (function.IsEmpty() || function->IsUndefined()) {
139 if (isUndefined) *isUndefined = true;
140 return v8::Local<v8::Value>();
143 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
145 Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
146 GuardCapture capture(context->engine, this);
148 QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
149 ep->propertyCapture = notifyOnValueChanged()?&capture:0;
152 if (notifyOnValueChanged())
153 capture.guards.copyAndClearPrepend(activeGuards);
155 QQmlContextData *lastSharedContext = 0;
156 QObject *lastSharedScope = 0;
158 bool sharedContext = useSharedContext();
160 // All code that follows must check with watcher before it accesses data members
161 // incase we have been deleted.
162 DeleteWatcher watcher(this);
165 lastSharedContext = ep->sharedContext;
166 lastSharedScope = ep->sharedScope;
167 ep->sharedContext = context;
168 ep->sharedScope = scopeObject();
171 v8::Local<v8::Value> result;
173 v8::TryCatch try_catch;
174 v8::Handle<v8::Object> This = ep->v8engine()->global();
175 if (scopeObject() && requiresThisObject()) {
176 v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject());
177 if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
180 result = function->Call(This, argc, args);
183 *isUndefined = try_catch.HasCaught() || result->IsUndefined();
185 if (watcher.wasDeleted()) {
186 } else if (try_catch.HasCaught()) {
187 v8::Context::Scope scope(ep->v8engine()->context());
188 v8::Local<v8::Message> message = try_catch.Message();
189 if (!message.IsEmpty()) {
190 delayedError()->setMessage(message);
192 if (hasDelayedError()) delayedError()->clearError();
195 if (hasDelayedError()) delayedError()->clearError();
200 ep->sharedContext = lastSharedContext;
201 ep->sharedScope = lastSharedScope;
204 if (capture.errorString) {
205 for (int ii = 0; ii < capture.errorString->count(); ++ii)
206 qWarning("%s", qPrintable(capture.errorString->at(ii)));
207 delete capture.errorString;
208 capture.errorString = 0;
211 while (Guard *g = capture.guards.takeFirst())
214 ep->propertyCapture = lastPropertyCapture;
219 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n)
223 // Try and find a matching guard
224 while (!guards.isEmpty() && !guards.first()->isConnected(n))
225 guards.takeFirst()->Delete();
228 if (!guards.isEmpty()) {
229 g = guards.takeFirst();
231 Q_ASSERT(g->isConnected(n));
233 g = Guard::New(expression, engine);
237 expression->activeGuards.prepend(g);
244 \a n is in the signal index range (see QObjectPrivate::signalIndex()).
246 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
251 errorString = new QStringList;
252 QString preamble = QLatin1String("QQmlExpression: Expression ") +
253 expression->m_vtable->expressionIdentifier(expression) +
254 QLatin1String(" depends on non-NOTIFYable properties:");
255 errorString->append(preamble);
258 const QMetaObject *metaObj = o->metaObject();
259 QMetaProperty metaProp = metaObj->property(c);
261 QString error = QLatin1String(" ") +
262 QString::fromUtf8(metaObj->className()) +
263 QLatin1String("::") +
264 QString::fromUtf8(metaProp.name());
265 errorString->append(error);
268 // Try and find a matching guard
269 while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
270 guards.takeFirst()->Delete();
273 if (!guards.isEmpty()) {
274 g = guards.takeFirst();
276 Q_ASSERT(g->isConnected(o, n));
278 g = Guard::New(expression, engine);
279 g->connect(o, n, engine);
282 expression->activeGuards.prepend(g);
287 void QQmlJavaScriptExpression::clearError()
289 if (m_vtable.hasValue()) {
290 m_vtable.value().clearError();
291 m_vtable.value().removeError();
295 QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
297 if (m_vtable.hasValue()) return m_vtable.constValue()->error(engine);
298 else return QQmlError();
301 QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
303 return &m_vtable.value();
306 void QQmlJavaScriptExpression::exceptionToError(v8::Handle<v8::Message> message, QQmlError &error)
308 Q_ASSERT(!message.IsEmpty());
310 v8::Handle<v8::Value> name = message->GetScriptResourceName();
311 v8::Handle<v8::String> description = message->Get();
312 int lineNumber = message->GetLineNumber();
314 v8::Local<v8::String> file = name->IsString()?name->ToString():v8::Local<v8::String>();
315 if (file.IsEmpty() || file->Length() == 0)
316 error.setUrl(QUrl());
318 error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
320 error.setLine(lineNumber);
323 QString qDescription = QV8Engine::toStringStatic(description);
324 if (qDescription.startsWith(QLatin1String("Uncaught ")))
325 qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
327 error.setDescription(qDescription);
330 // Callee owns the persistent handle
331 v8::Persistent<v8::Function>
332 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
333 const char *code, int codeLength,
334 const QString &filename, quint16 line,
335 v8::Persistent<v8::Object> *qmlscope)
337 QQmlEngine *engine = ctxt->engine;
338 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
340 v8::HandleScope handle_scope;
341 v8::Context::Scope ctxtscope(ep->v8engine()->context());
344 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
345 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, codeLength, filename, line);
346 if (tc.HasCaught()) {
348 error.setDescription(QLatin1String("Exception occurred during function compilation"));
350 error.setUrl(QUrl::fromLocalFile(filename));
351 v8::Local<v8::Message> message = tc.Message();
352 if (!message.IsEmpty())
353 QQmlExpressionPrivate::exceptionToError(message, error);
355 return v8::Persistent<v8::Function>();
357 v8::Local<v8::Value> result = script->Run(scopeobject);
358 if (tc.HasCaught()) {
360 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
362 error.setUrl(QUrl::fromLocalFile(filename));
363 v8::Local<v8::Message> message = tc.Message();
364 if (!message.IsEmpty())
365 QQmlExpressionPrivate::exceptionToError(message, error);
367 return v8::Persistent<v8::Function>();
369 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
370 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
373 // Callee owns the persistent handle
374 v8::Persistent<v8::Function>
375 QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
376 const QString &code, const QString &filename, quint16 line,
377 v8::Persistent<v8::Object> *qmlscope)
379 QQmlEngine *engine = ctxt->engine;
380 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
382 v8::HandleScope handle_scope;
383 v8::Context::Scope ctxtscope(ep->v8engine()->context());
386 v8::Local<v8::Object> scopeobject = ep->v8engine()->qmlScope(ctxt, scope);
387 v8::Local<v8::Script> script = ep->v8engine()->qmlModeCompile(code, filename, line);
388 if (tc.HasCaught()) {
390 error.setDescription(QLatin1String("Exception occurred during function compilation"));
392 error.setUrl(QUrl::fromLocalFile(filename));
393 v8::Local<v8::Message> message = tc.Message();
394 if (!message.IsEmpty())
395 QQmlExpressionPrivate::exceptionToError(message, error);
397 return v8::Persistent<v8::Function>();
399 v8::Local<v8::Value> result = script->Run(scopeobject);
400 if (tc.HasCaught()) {
402 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
404 error.setUrl(QUrl::fromLocalFile(filename));
405 v8::Local<v8::Message> message = tc.Message();
406 if (!message.IsEmpty())
407 QQmlExpressionPrivate::exceptionToError(message, error);
409 return v8::Persistent<v8::Function>();
411 if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
412 return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
415 void QQmlJavaScriptExpression::clearGuards()
417 while (Guard *g = activeGuards.takeFirst())
421 void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
423 QQmlJavaScriptExpression *expression =
424 static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression;
426 expression->m_vtable->expressionChanged(expression);