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 "qv8bindings_p.h"
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>
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
65 static QQmlJavaScriptExpression::VTable QV8Bindings_Binding_jsvtable = {
66 QV8Bindings::Binding::expressionIdentifier,
67 QV8Bindings::Binding::expressionChanged
70 QV8Bindings::Binding::Binding()
71 : QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), QQmlAbstractBinding(V8), parent(0)
75 void QV8Bindings::Binding::setEnabled(QQmlAbstractBinding *_This, bool e,
76 QQmlPropertyPrivate::WriteFlags flags)
78 QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
80 if (This->enabledFlag() != e) {
81 This->setEnabledFlag(e);
83 if (e) This->update(flags);
87 void QV8Bindings::refresh()
89 int count = functions()->Length();
90 for (int ii = 0; ii < count; ++ii)
91 bindings[ii].refresh();
94 void QV8Bindings::Binding::refresh()
99 int QV8Bindings::Binding::propertyIndex(const QQmlAbstractBinding *_This)
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();
106 QObject *QV8Bindings::Binding::object(const QQmlAbstractBinding *_This)
108 const QV8Bindings::Binding *This = static_cast<const QV8Bindings::Binding *>(_This);
110 if (This->target.hasValue()) return This->target.constValue()->target;
111 else return *This->target;
114 QObject *QV8Bindings::Binding::object() const
116 if (target.hasValue()) return target.constValue()->target;
120 void QV8Bindings::Binding::retargetBinding(QQmlAbstractBinding *_This, QObject *t, int i)
122 QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
124 This->target.value().target = t;
125 This->target.value().targetProperty = i;
128 void QV8Bindings::Binding::update(QQmlAbstractBinding *_This, QQmlPropertyPrivate::WriteFlags flags)
130 QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
134 void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
139 QQmlContextData *context = parent->context();
140 if (!context || !context->isValid())
143 // Check that the target has not been deleted
144 if (QQmlData::wasDeleted(object()))
147 int lineNo = qmlSourceCoordinate(instruction->line);
148 int columnNo = qmlSourceCoordinate(instruction->column);
150 QQmlTrace trace("V8 Binding Update");
151 trace.addDetail("URL", parent->url());
152 trace.addDetail("Line", lineNo);
153 trace.addDetail("Column", columnNo);
155 QQmlBindingProfiler prof(parent->urlString(), lineNo, columnNo, QQmlProfilerService::V8Binding);
157 if (!updatingFlag()) {
158 setUpdatingFlag(true);
160 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
162 bool isUndefined = false;
164 DeleteWatcher watcher(this);
165 ep->referenceScarceResources();
167 v8::HandleScope handle_scope;
168 v8::Context::Scope scope(ep->v8engine()->context());
169 v8::Local<v8::Value> result =
171 v8::Handle<v8::Function>::Cast(parent->functions()->Get(instruction->value)),
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,
182 if (!watcher.wasDeleted() && !destroyedFlag()) {
184 if (needsErrorLocationData)
185 delayedError()->setErrorLocation(parent->url(), instruction->line, 0);
188 if (!delayedError()->addError(ep)) ep->warning(this->error(context->engine));
193 setUpdatingFlag(false);
196 ep->dereferenceScarceResources();
199 QQmlProperty p = QQmlPropertyPrivate::restore(*target, instruction->property, context);
200 QQmlAbstractBinding::printBindingLoopError(p);
204 QString QV8Bindings::Binding::expressionIdentifier(QQmlJavaScriptExpression *e)
206 Binding *This = static_cast<Binding *>(e);
207 return This->parent->urlString() + QLatin1Char(':') +
208 QString::number(qmlSourceCoordinate(This->instruction->line));
211 void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e)
213 Binding *This = static_cast<Binding *>(e);
214 This->update(QQmlPropertyPrivate::DontRemoveBinding);
217 void QV8Bindings::Binding::destroy(QQmlAbstractBinding *_This)
219 QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This);
221 This->setEnabledFlag(false);
222 This->setDestroyedFlag(true);
223 This->removeFromObject();
226 This->parent->release();
229 QV8Bindings::QV8Bindings(QQmlCompiledData::V8Program *program,
231 QQmlContextData *context)
232 : program(program), bindings(0), refCount(1)
234 QV8Engine *engine = QQmlEnginePrivate::getV8Engine(context->engine);
236 if (program->bindings.IsEmpty()) {
237 v8::HandleScope handle_scope;
238 v8::Context::Scope scope(engine->context());
240 v8::Local<v8::Script> script;
241 bool compileFailed = false;
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;
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());
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
272 int bindingsCount = functions()->Length();
273 if (bindingsCount) bindings = new QV8Bindings::Binding[bindingsCount];
278 QV8Bindings::~QV8Bindings()
286 QQmlAbstractBinding *
287 QV8Bindings::configBinding(QObject *target, QObject *scope,
288 const QQmlInstruction::instr_assignBinding *i)
290 if (!bindings) // initialization failed.
293 QV8Bindings::Binding *rv = bindings + i->value;
297 rv->setScopeObject(scope);
298 rv->setUseSharedContext(true);
299 rv->setNotifyOnValueChanged(true);
303 addref(); // This is decremented in Binding::destroy()
308 const QUrl &QV8Bindings::url() const
310 return program->cdata->url;
313 const QString &QV8Bindings::urlString() const
315 return program->cdata->name;
318 v8::Persistent<v8::Array> &QV8Bindings::functions() const
320 return program->bindings;