Add missing QT_{BEGIN,END}_NAMESPACE
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmljavascriptexpression.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmljavascriptexpression_p.h"
43
44 #include <private/qqmlexpression_p.h>
45
46 QT_BEGIN_NAMESPACE
47
48 bool QQmlDelayedError::addError(QQmlEnginePrivate *e)
49 {
50     if (!e) return false;
51
52     if (e->inProgressCreations == 0) return false; // Not in construction
53
54     if (prevError) return true; // Already in error chain
55
56     prevError = &e->erroredBindings;
57     nextError = e->erroredBindings;
58     e->erroredBindings = this;
59     if (nextError) nextError->prevError = &nextError;
60
61     return true;
62 }
63
64 void QQmlDelayedError::setMessage(v8::Handle<v8::Message> message)
65 {
66     qPersistentDispose(m_message);
67     m_message = qPersistentNew<v8::Message>(message);
68 }
69
70 void QQmlDelayedError::setErrorLocation(const QUrl &url, quint16 line, quint16 column)
71 {
72     m_error.setUrl(url);
73     m_error.setLine(line);
74     m_error.setColumn(column);
75 }
76
77 void QQmlDelayedError::setErrorDescription(const QString &description)
78 {
79     m_error.setDescription(description);
80 }
81
82 /*
83     Converting from a message to an error is relatively expensive.
84
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.
89 */
90 void QQmlDelayedError::convertMessageToError(QQmlEngine *engine) const
91 {
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);
97     }
98 }
99
100 QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v)
101 : m_vtable(v)
102 {
103 }
104
105 QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
106 {
107     clearGuards();
108     if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
109         m_scopeObject.asT2()->_s = 0;
110 }
111
112 void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
113 {
114     activeGuards.setFlagValue(v);
115     if (!v) clearGuards();
116 }
117
118 void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
119 {
120     clearGuards();
121 }
122
123 v8::Local<v8::Value>
124 QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
125                                    v8::Handle<v8::Function> function, bool *isUndefined)
126 {
127     Q_ASSERT(context && context->engine);
128
129     if (function.IsEmpty() || function->IsUndefined()) {
130         if (isUndefined) *isUndefined = true;
131         return v8::Local<v8::Value>();
132     }
133
134     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
135
136     Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
137     GuardCapture capture(context->engine, this);
138
139     QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
140     ep->propertyCapture = notifyOnValueChanged()?&capture:0;
141
142
143     if (notifyOnValueChanged())
144         capture.guards.copyAndClearPrepend(activeGuards);
145
146     QQmlContextData *lastSharedContext = 0;
147     QObject *lastSharedScope = 0;
148
149     bool sharedContext = useSharedContext();
150
151     // All code that follows must check with watcher before it accesses data members
152     // incase we have been deleted.
153     DeleteWatcher watcher(this);
154
155     if (sharedContext) {
156         lastSharedContext = ep->sharedContext;
157         lastSharedScope = ep->sharedScope;
158         ep->sharedContext = context;
159         ep->sharedScope = scopeObject();
160     }
161
162     v8::Local<v8::Value> result;
163     {
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);
169         }
170
171         result = function->Call(This, 0, 0);
172
173         if (isUndefined)
174             *isUndefined = try_catch.HasCaught() || result->IsUndefined();
175
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);
182             } else {
183                 if (hasDelayedError()) delayedError()->clearError();
184             }
185         } else {
186             if (hasDelayedError()) delayedError()->clearError();
187         }
188     }
189
190     if (sharedContext) {
191         ep->sharedContext = lastSharedContext;
192         ep->sharedScope = lastSharedScope;
193     }
194
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;
200     }
201
202     while (Guard *g = capture.guards.takeFirst())
203         g->Delete();
204
205     ep->propertyCapture = lastPropertyCapture;
206
207     return result;
208 }
209
210 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n)
211 {
212     if (expression) {
213
214         // Try and find a matching guard
215         while (!guards.isEmpty() && !guards.first()->isConnected(n))
216             guards.takeFirst()->Delete();
217
218         Guard *g = 0;
219         if (!guards.isEmpty()) {
220             g = guards.takeFirst();
221             g->cancelNotify();
222             Q_ASSERT(g->isConnected(n));
223         } else {
224             g = Guard::New(expression, engine);
225             g->connect(n);
226         }
227
228         expression->activeGuards.prepend(g);
229     }
230 }
231
232 /*! \internal
233     \reimp
234
235     \a n is in the signal index range (see QObjectPrivate::signalIndex()).
236 */
237 void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
238 {
239     if (expression) {
240         if (n == -1) {
241             if (!errorString) {
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);
247             }
248
249             const QMetaObject *metaObj = o->metaObject();
250             QMetaProperty metaProp = metaObj->property(c);
251
252             QString error = QLatin1String("    ") +
253                             QString::fromUtf8(metaObj->className()) +
254                             QLatin1String("::") +
255                             QString::fromUtf8(metaProp.name());
256             errorString->append(error);
257         } else {
258
259             // Try and find a matching guard
260             while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
261                 guards.takeFirst()->Delete();
262
263             Guard *g = 0;
264             if (!guards.isEmpty()) {
265                 g = guards.takeFirst();
266                 g->cancelNotify();
267                 Q_ASSERT(g->isConnected(o, n));
268             } else {
269                 g = Guard::New(expression, engine);
270                 g->connect(o, n, engine);
271             }
272
273             expression->activeGuards.prepend(g);
274         }
275     }
276 }
277
278 void QQmlJavaScriptExpression::clearError()
279 {
280     if (m_vtable.hasValue()) {
281         m_vtable.value().clearError();
282         m_vtable.value().removeError();
283     }
284 }
285
286 QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
287 {
288     if (m_vtable.hasValue()) return m_vtable.constValue()->error(engine);
289     else return QQmlError();
290 }
291
292 QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
293 {
294     return &m_vtable.value();
295 }
296
297 void QQmlJavaScriptExpression::exceptionToError(v8::Handle<v8::Message> message, QQmlError &error)
298 {
299     Q_ASSERT(!message.IsEmpty());
300
301     v8::Handle<v8::Value> name = message->GetScriptResourceName();
302     v8::Handle<v8::String> description = message->Get();
303     int lineNumber = message->GetLineNumber();
304
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());
308     else
309         error.setUrl(QUrl(QV8Engine::toStringStatic(file)));
310
311     error.setLine(lineNumber);
312     error.setColumn(-1);
313
314     QString qDescription = QV8Engine::toStringStatic(description);
315     if (qDescription.startsWith(QLatin1String("Uncaught ")))
316         qDescription = qDescription.mid(9 /* strlen("Uncaught ") */);
317
318     error.setDescription(qDescription);
319 }
320
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, quint16 line,
326                                        v8::Persistent<v8::Object> *qmlscope)
327 {
328     QQmlEngine *engine = ctxt->engine;
329     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
330
331     v8::HandleScope handle_scope;
332     v8::Context::Scope ctxtscope(ep->v8engine()->context());
333
334     v8::TryCatch tc;
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()) {
338         QQmlError error;
339         error.setDescription(QLatin1String("Exception occurred during function compilation"));
340         error.setLine(line);
341         error.setUrl(QUrl::fromLocalFile(filename));
342         v8::Local<v8::Message> message = tc.Message();
343         if (!message.IsEmpty())
344             QQmlExpressionPrivate::exceptionToError(message, error);
345         ep->warning(error);
346         return v8::Persistent<v8::Function>();
347     }
348     v8::Local<v8::Value> result = script->Run(scopeobject);
349     if (tc.HasCaught()) {
350         QQmlError error;
351         error.setDescription(QLatin1String("Exception occurred during function evaluation"));
352         error.setLine(line);
353         error.setUrl(QUrl::fromLocalFile(filename));
354         v8::Local<v8::Message> message = tc.Message();
355         if (!message.IsEmpty())
356             QQmlExpressionPrivate::exceptionToError(message, error);
357         ep->warning(error);
358         return v8::Persistent<v8::Function>();
359     }
360     if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
361     return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
362 }
363
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, quint16 line,
368                                        v8::Persistent<v8::Object> *qmlscope)
369 {
370     QQmlEngine *engine = ctxt->engine;
371     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
372
373     v8::HandleScope handle_scope;
374     v8::Context::Scope ctxtscope(ep->v8engine()->context());
375
376     v8::TryCatch tc;
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()) {
380         QQmlError error;
381         error.setDescription(QLatin1String("Exception occurred during function compilation"));
382         error.setLine(line);
383         error.setUrl(QUrl::fromLocalFile(filename));
384         v8::Local<v8::Message> message = tc.Message();
385         if (!message.IsEmpty())
386             QQmlExpressionPrivate::exceptionToError(message, error);
387         ep->warning(error);
388         return v8::Persistent<v8::Function>();
389     }
390     v8::Local<v8::Value> result = script->Run(scopeobject);
391     if (tc.HasCaught()) {
392         QQmlError error;
393         error.setDescription(QLatin1String("Exception occurred during function evaluation"));
394         error.setLine(line);
395         error.setUrl(QUrl::fromLocalFile(filename));
396         v8::Local<v8::Message> message = tc.Message();
397         if (!message.IsEmpty())
398             QQmlExpressionPrivate::exceptionToError(message, error);
399         ep->warning(error);
400         return v8::Persistent<v8::Function>();
401     }
402     if (qmlscope) *qmlscope = qPersistentNew<v8::Object>(scopeobject);
403     return qPersistentNew<v8::Function>(v8::Local<v8::Function>::Cast(result));
404 }
405
406 void QQmlJavaScriptExpression::clearGuards()
407 {
408     while (Guard *g = activeGuards.takeFirst())
409         g->Delete();
410 }
411
412 void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
413 {
414     QQmlJavaScriptExpression *expression =
415         static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression;
416
417     expression->m_vtable->expressionChanged(expression);
418 }
419
420 QT_END_NAMESPACE