9999ebc7fa6eea8b3c0e496678f2c02d4928bf64
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8bindings.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 "qv8bindings_p.h"
43
44 #include <private/qv8_p.h>
45 #include <private/qqmlbinding_p.h>
46 #include <private/qqmlcompiler_p.h>
47 #include <private/qqmlproperty_p.h>
48 #include <private/qqmlexpression_p.h>
49 #include <private/qobject_p.h>
50 #include <private/qqmltrace_p.h>
51 #include <private/qqmlprofilerservice_p.h>
52
53 QT_BEGIN_NAMESPACE
54
55 QQmlAbstractBinding::VTable QV8Bindings_Binding_vtable = {
56     QV8Bindings::Binding::destroy,
57     QQmlAbstractBinding::default_expression,
58     QV8Bindings::Binding::propertyIndex,
59     QV8Bindings::Binding::object,
60     QV8Bindings::Binding::setEnabled,
61     QV8Bindings::Binding::update,
62     QV8Bindings::Binding::retargetBinding
63 };
64
65 static QQmlJavaScriptExpression::VTable QV8Bindings_Binding_jsvtable = {
66     QV8Bindings::Binding::expressionIdentifier,
67     QV8Bindings::Binding::expressionChanged
68 };
69
70 QV8Bindings::Binding::Binding()
71 : QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), QQmlAbstractBinding(V8), parent(0)
72 {
73 }
74
75 void QV8Bindings::Binding::setEnabled(QQmlAbstractBinding *_This, bool e,
76                                       QQmlPropertyPrivate::WriteFlags flags)
77 {
78     QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
79
80     if (This->enabledFlag() != e) {
81         This->setEnabledFlag(e);
82
83         if (e) This->update(flags);
84     }
85 }
86
87 void QV8Bindings::refresh()
88 {
89     int count = functions()->Length();
90     for (int ii = 0; ii < count; ++ii)
91         bindings[ii].refresh();
92 }
93
94 void QV8Bindings::Binding::refresh()
95 {
96     update();
97 }
98
99 int QV8Bindings::Binding::propertyIndex(const QQmlAbstractBinding *_This)
100 {
101     const QV8Bindings::Binding *This = static_cast<const QV8Bindings::Binding *>(_This);
102     if (This->target.hasValue()) return This->target.constValue()->targetProperty;
103     else return This->instruction->property.encodedIndex();
104 }
105
106 QObject *QV8Bindings::Binding::object(const QQmlAbstractBinding *_This)
107 {
108     const QV8Bindings::Binding *This = static_cast<const QV8Bindings::Binding *>(_This);
109
110     if (This->target.hasValue()) return This->target.constValue()->target;
111     else return *This->target;
112 }
113
114 QObject *QV8Bindings::Binding::object() const
115 {
116     if (target.hasValue()) return target.constValue()->target;
117     else return *target;
118 }
119
120 void QV8Bindings::Binding::retargetBinding(QQmlAbstractBinding *_This, QObject *t, int i)
121 {
122     QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
123
124     This->target.value().target = t;
125     This->target.value().targetProperty = i;
126 }
127
128 void QV8Bindings::Binding::update(QQmlAbstractBinding *_This, QQmlPropertyPrivate::WriteFlags flags)
129 {
130     QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
131     This->update(flags);
132 }
133
134 void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
135 {
136     if (!enabledFlag())
137         return;
138
139     QQmlContextData *context = parent->context();
140     if (!context || !context->isValid())
141         return;
142
143     // Check that the target has not been deleted
144     if (QQmlData::wasDeleted(object()))
145         return;
146
147     int lineNo = qmlSourceCoordinate(instruction->line);
148     int columnNo = qmlSourceCoordinate(instruction->column);
149
150     QQmlTrace trace("V8 Binding Update");
151     trace.addDetail("URL", parent->url());
152     trace.addDetail("Line", lineNo);
153     trace.addDetail("Column", columnNo);
154
155     QQmlBindingProfiler prof(parent->urlString(), lineNo, columnNo, QQmlProfilerService::V8Binding);
156
157     if (!updatingFlag()) {
158         setUpdatingFlag(true);
159
160         QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
161
162         bool isUndefined = false;
163
164         DeleteWatcher watcher(this);
165         ep->referenceScarceResources(); 
166
167         v8::HandleScope handle_scope;
168         v8::Context::Scope scope(ep->v8engine()->context());
169         v8::Local<v8::Value> result =
170             evaluate(context,
171                      v8::Handle<v8::Function>::Cast(parent->functions()->Get(instruction->value)),
172                      &isUndefined);
173
174         trace.event("writing V8 result");
175         bool needsErrorLocationData = false;
176         if (!watcher.wasDeleted() && !destroyedFlag() && !hasError()) {
177             typedef QQmlPropertyPrivate PP;
178             needsErrorLocationData = !PP::writeBinding(*target, instruction->property, context, this, result,
179                                                isUndefined, flags);
180         }
181
182         if (!watcher.wasDeleted() && !destroyedFlag()) {
183
184             if (needsErrorLocationData)
185                 delayedError()->setErrorLocation(parent->url(), instruction->line, 0);
186
187             if (hasError()) {
188                 if (!delayedError()->addError(ep)) ep->warning(this->error(context->engine));
189             } else {
190                 clearError();
191             }
192
193             setUpdatingFlag(false);
194         }
195
196         ep->dereferenceScarceResources(); 
197
198     } else {
199         QQmlProperty p = QQmlPropertyPrivate::restore(*target, instruction->property, context);
200         QQmlAbstractBinding::printBindingLoopError(p);
201     }
202 }
203
204 QString QV8Bindings::Binding::expressionIdentifier(QQmlJavaScriptExpression *e)
205 {
206     Binding *This = static_cast<Binding *>(e);
207     return This->parent->urlString() + QLatin1Char(':') +
208            QString::number(qmlSourceCoordinate(This->instruction->line));
209 }
210
211 void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e)
212 {
213     Binding *This = static_cast<Binding *>(e);
214     This->update(QQmlPropertyPrivate::DontRemoveBinding);
215 }
216
217 void QV8Bindings::Binding::destroy(QQmlAbstractBinding *_This)
218 {
219     QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
220
221     This->setEnabledFlag(false);
222     This->setDestroyedFlag(true);
223     This->removeFromObject();
224     This->clear();
225     This->clearError();
226     This->parent->release();
227 }
228
229 QV8Bindings::QV8Bindings(QQmlCompiledData::V8Program *program,
230                          quint16 line,
231                          QQmlContextData *context)
232 : program(program), bindings(0), refCount(1)
233 {
234     QV8Engine *engine = QQmlEnginePrivate::getV8Engine(context->engine);
235
236     if (program->bindings.IsEmpty()) {
237         v8::HandleScope handle_scope;
238         v8::Context::Scope scope(engine->context());
239
240         v8::Local<v8::Script> script;
241         bool compileFailed = false;
242         {
243             v8::TryCatch try_catch;
244             const QByteArray &source = program->program;
245             script = engine->qmlModeCompile(source.constData(), source.length(),
246                                             program->cdata->name, line);
247             if (try_catch.HasCaught()) {
248                 // The binding was not compiled.  There are some exceptional cases which the
249                 // expression rewriter does not rewrite properly (e.g., \r-terminated lines
250                 // are not rewritten correctly but this bug is demed out-of-scope to fix for
251                 // performance reasons; see QTBUG-24064).
252                 compileFailed = true;
253                 QQmlError error;
254                 error.setDescription(QString(QLatin1String("Exception occurred during compilation of binding at line: %1")).arg(line));
255                 v8::Local<v8::Message> message = try_catch.Message();
256                 if (!message.IsEmpty())
257                     QQmlExpressionPrivate::exceptionToError(message, error);
258                 QQmlEnginePrivate::get(engine->engine())->warning(error);
259                 program->bindings = qPersistentNew(v8::Array::New());
260             }
261         }
262
263         if (!compileFailed) {
264             v8::Local<v8::Value> result = script->Run(engine->contextWrapper()->sharedContext());
265             if (result->IsArray()) {
266                 program->bindings = qPersistentNew(v8::Local<v8::Array>::Cast(result));
267                 program->program.clear(); // We don't need the source anymore
268             }
269         }
270     }
271
272     int bindingsCount = functions()->Length();
273     if (bindingsCount) bindings = new QV8Bindings::Binding[bindingsCount];
274
275     setContext(context);
276 }
277
278 QV8Bindings::~QV8Bindings()
279 {
280     program = 0;
281
282     delete [] bindings;
283     bindings = 0;
284 }
285
286 QQmlAbstractBinding *
287 QV8Bindings::configBinding(QObject *target, QObject *scope,
288                            const QQmlInstruction::instr_assignBinding *i)
289 {
290     if (!bindings) // initialization failed.
291         return 0;
292
293     QV8Bindings::Binding *rv = bindings + i->value;
294
295     rv->instruction = i;
296     rv->target = target;
297     rv->setScopeObject(scope);
298     rv->setUseSharedContext(true);
299     rv->setNotifyOnValueChanged(true);
300     rv->parent = this;
301
302     if (!i->isFallback)
303         addref(); // This is decremented in Binding::destroy()
304
305     return rv;
306 }
307
308 const QUrl &QV8Bindings::url() const
309 {
310     return program->cdata->url;
311 }
312
313 const QString &QV8Bindings::urlString() const
314 {
315     return program->cdata->name;
316 }
317
318 v8::Persistent<v8::Array> &QV8Bindings::functions() const
319 {
320     return program->bindings;
321 }
322
323
324 QT_END_NAMESPACE