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 "qqmlvme_p.h"
44 #include "qqmlcompiler_p.h"
45 #include "qqmlboundsignal_p.h"
46 #include "qqmlstringconverters_p.h"
47 #include <private/qmetaobjectbuilder_p.h>
48 #include <private/qfastmetabuilder_p.h>
49 #include "qqmldata_p.h"
51 #include "qqmlcustomparser_p.h"
52 #include "qqmlengine.h"
53 #include "qqmlcontext.h"
54 #include "qqmlcomponent.h"
55 #include "qqmlcomponentattached_p.h"
56 #include "qqmlbinding_p.h"
57 #include "qqmlengine_p.h"
58 #include "qqmlcomponent_p.h"
59 #include "qqmlvmemetaobject_p.h"
60 #include "qqmlcontext_p.h"
61 #include <private/qv4bindings_p.h>
62 #include <private/qv8bindings_p.h>
63 #include "qqmlglobal_p.h"
64 #include <private/qfinitestack_p.h>
65 #include "qqmlscriptstring.h"
66 #include "qqmlscriptstring_p.h"
67 #include "qqmlpropertyvalueinterceptor_p.h"
68 #include "qqmlvaluetypeproxybinding_p.h"
75 #include <QtCore/qdebug.h>
76 #include <QtCore/qvarlengtharray.h>
77 #include <QtCore/qcoreapplication.h>
78 #include <QtCore/qdatetime.h>
79 #include <QtCore/qvarlengtharray.h>
80 #include <QtQml/qjsvalue.h>
84 using namespace QQmlVMETypes;
86 #define VME_EXCEPTION(desc, line) \
89 error.setDescription(desc.trimmed()); \
90 error.setLine(line); \
91 error.setUrl(COMP->url); \
96 void QQmlVME::init(QQmlContextData *ctxt, QQmlCompiledData *comp, int start,
97 QQmlContextData *creation)
105 creationContext = creation;
109 initState.context = ctxt;
110 initState.compiledData = comp;
111 initState.instructionStream = comp->bytecode.constData() + start;
112 states.push(initState);
114 typedef QQmlInstruction I;
115 I *i = (I *)initState.instructionStream;
117 Q_ASSERT(comp->instructionType(i) == I::Init);
119 objects.allocate(i->init.objectStackSize);
120 lists.allocate(i->init.listStackSize);
121 bindValues.allocate(i->init.bindingsSize);
122 parserStatus.allocate(i->init.parserStatusSize);
124 #ifdef QML_ENABLE_TRACE
125 parserStatusData.allocate(i->init.parserStatusSize);
126 rootComponent = comp;
130 engine = ctxt->engine;
133 bool QQmlVME::initDeferred(QObject *object)
135 QQmlData *data = QQmlData::get(object);
137 if (!data || !data->context || !data->deferredComponent)
140 QQmlContextData *ctxt = data->context;
141 QQmlCompiledData *comp = data->deferredComponent;
142 int start = data->deferredIdx;
145 initState.flags = State::Deferred;
146 initState.context = ctxt;
147 initState.compiledData = comp;
148 initState.instructionStream = comp->bytecode.constData() + start;
149 states.push(initState);
151 typedef QQmlInstruction I;
152 I *i = (I *)initState.instructionStream;
154 Q_ASSERT(comp->instructionType(i) == I::DeferInit);
156 objects.allocate(i->deferInit.objectStackSize);
157 lists.allocate(i->deferInit.listStackSize);
158 bindValues.allocate(i->deferInit.bindingsSize);
159 parserStatus.allocate(i->deferInit.parserStatusSize);
161 objects.push(object);
163 #ifdef QML_ENABLE_TRACE
164 parserStatusData.allocate(i->deferInit.parserStatusSize);
165 rootComponent = comp;
169 engine = ctxt->engine;
175 struct ActiveVMERestorer
177 ActiveVMERestorer(QQmlVME *me, QQmlEnginePrivate *ep)
178 : ep(ep), oldVME(ep->activeVME) { ep->activeVME = me; }
179 ~ActiveVMERestorer() { ep->activeVME = oldVME; }
181 QQmlEnginePrivate *ep;
186 QObject *QQmlVME::execute(QList<QQmlError> *errors, const Interrupt &interrupt)
188 Q_ASSERT(states.count() >= 1);
190 #ifdef QML_ENABLE_TRACE
191 QQmlTrace trace("VME Execute");
192 trace.addDetail("URL", rootComponent->url);
195 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(states.at(0).context->engine);
197 ActiveVMERestorer restore(this, ep);
199 QObject *rv = run(errors, interrupt);
204 inline bool fastHasBinding(QObject *o, int index)
206 QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(o)->declarativeData);
208 return ddata && (ddata->bindingBitsSize > index) &&
209 (ddata->bindingBits[index / 32] & (1 << (index % 32)));
212 static void removeBindingOnProperty(QObject *o, int index)
214 QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, index, -1, 0);
215 if (binding) binding->destroy();
218 static QVariant variantFromString(const QString &string)
220 return QQmlStringConverters::variantFromString(string);
223 // XXX we probably need some form of "work count" here to prevent us checking this
224 // for every instruction.
225 #define QML_BEGIN_INSTR_COMMON(I) { \
226 const QQmlInstructionMeta<(int)QQmlInstruction::I>::DataType &instr = QQmlInstructionMeta<(int)QQmlInstruction::I>::data(*genericInstr); \
227 INSTRUCTIONSTREAM += QQmlInstructionMeta<(int)QQmlInstruction::I>::Size; \
230 #ifdef QML_THREADED_VME_INTERPRETER
231 # define QML_BEGIN_INSTR(I) op_##I: \
232 QML_BEGIN_INSTR_COMMON(I)
234 # define QML_NEXT_INSTR(I) { \
235 if (watcher.hasRecursed()) return 0; \
236 genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \
237 goto *genericInstr->common.code; \
240 # define QML_END_INSTR(I) } \
241 if (watcher.hasRecursed()) return 0; \
242 genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \
243 if (interrupt.shouldInterrupt()) return 0; \
244 goto *genericInstr->common.code;
247 # define QML_BEGIN_INSTR(I) \
248 case QQmlInstruction::I: \
249 QML_BEGIN_INSTR_COMMON(I)
251 # define QML_NEXT_INSTR(I) { \
252 if (watcher.hasRecursed()) return 0; \
256 # define QML_END_INSTR(I) \
257 if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return 0; \
261 #define QML_STORE_VALUE(name, cpptype, value) \
262 QML_BEGIN_INSTR(name) \
264 void *a[] = { (void *)&v, 0, &status, &flags }; \
265 QObject *target = objects.top(); \
266 CLEAN_PROPERTY(target, instr.propertyIndex); \
267 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
270 #define QML_STORE_LIST(name, cpptype, value) \
271 QML_BEGIN_INSTR(name) \
274 void *a[] = { (void *)&v, 0, &status, &flags }; \
275 QObject *target = objects.top(); \
276 CLEAN_PROPERTY(target, instr.propertyIndex); \
277 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
280 #define QML_STORE_VAR(name, value) \
281 QML_BEGIN_INSTR(name) \
282 v8::Handle<v8::Value> v8value = value; \
283 QObject *target = objects.top(); \
284 CLEAN_PROPERTY(target, instr.propertyIndex); \
285 QMetaObject *mo = const_cast<QMetaObject *>(target->metaObject()); \
286 QQmlVMEMetaObject *vmemo = static_cast<QQmlVMEMetaObject *>(mo); \
287 vmemo->setVMEProperty(instr.propertyIndex, v8value); \
290 #define QML_STORE_POINTER(name, value) \
291 QML_BEGIN_INSTR(name) \
292 void *a[] = { (void *)value, 0, &status, &flags }; \
293 QObject *target = objects.top(); \
294 CLEAN_PROPERTY(target, instr.propertyIndex); \
295 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
298 #define CLEAN_PROPERTY(o, index) \
299 if (fastHasBinding(o, index)) \
300 removeBindingOnProperty(o, index)
302 QObject *QQmlVME::run(QList<QQmlError> *errors,
303 const Interrupt &interrupt
304 #ifdef QML_THREADED_VME_INTERPRETER
305 , void ***storeJumpTable
309 #ifdef QML_THREADED_VME_INTERPRETER
310 if (storeJumpTable) {
311 #define QML_INSTR_ADDR(I, FMT) &&op_##I,
312 static void *jumpTable[] = {
313 FOR_EACH_QML_INSTR(QML_INSTR_ADDR)
315 #undef QML_INSTR_ADDR
316 *storeJumpTable = jumpTable;
320 Q_ASSERT(errors->isEmpty());
321 Q_ASSERT(states.count() >= 1);
323 QQmlEngine *engine = states.at(0).context->engine;
324 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
326 // Need a v8 handle scope and execution context for StoreVar instructions.
327 v8::HandleScope handleScope;
328 v8::Context::Scope contextScope(ep->v8engine()->context());
330 int status = -1; // needed for dbus
331 QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::BypassInterceptor |
332 QQmlPropertyPrivate::RemoveBindingOnAliasWrite;
334 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
336 #define COMP states.top().compiledData
337 #define CTXT states.top().context
338 #define INSTRUCTIONSTREAM states.top().instructionStream
339 #define BINDINGSKIPLIST states.top().bindingSkipList
341 #define TYPES COMP->types
342 #define PRIMITIVES COMP->primitives
343 #define DATAS COMP->datas
344 #define PROGRAMS COMP->programs
345 #define PROPERTYCACHES COMP->propertyCaches
346 #define SCRIPTS COMP->scripts
347 #define URLS COMP->urls
349 #ifdef QML_THREADED_VME_INTERPRETER
350 const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM);
351 goto *genericInstr->common.code;
354 const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM);
356 switch (genericInstr->common.instructionType) {
359 // Store a created object in a property. These all pop from the objects stack.
360 QML_STORE_VALUE(StoreObject, QObject *, objects.pop());
361 QML_STORE_VALUE(StoreVariantObject, QVariant, QVariant::fromValue(objects.pop()));
362 QML_STORE_VAR(StoreVarObject, ep->v8engine()->newQObject(objects.pop()));
364 // Store a literal value in a corresponding property
365 QML_STORE_VALUE(StoreFloat, float, instr.value);
366 QML_STORE_VALUE(StoreDouble, double, instr.value);
367 QML_STORE_VALUE(StoreBool, bool, instr.value);
368 QML_STORE_VALUE(StoreInteger, int, instr.value);
369 QML_STORE_VALUE(StoreColor, QColor, QColor::fromRgba(instr.value));
370 QML_STORE_VALUE(StoreDate, QDate, QDate::fromJulianDay(instr.value));
371 QML_STORE_VALUE(StoreDateTime, QDateTime,
372 QDateTime(QDate::fromJulianDay(instr.date), *(QTime *)&instr.time));
373 QML_STORE_POINTER(StoreTime, (QTime *)&instr.time);
374 QML_STORE_POINTER(StorePoint, (QPoint *)&instr.point);
375 QML_STORE_POINTER(StorePointF, (QPointF *)&instr.point);
376 QML_STORE_POINTER(StoreSize, (QSize *)&instr.size);
377 QML_STORE_POINTER(StoreSizeF, (QSizeF *)&instr.size);
378 QML_STORE_POINTER(StoreRect, (QRect *)&instr.rect);
379 QML_STORE_POINTER(StoreRectF, (QRectF *)&instr.rect);
380 QML_STORE_POINTER(StoreVector3D, (QVector3D *)&instr.vector);
381 QML_STORE_POINTER(StoreVector4D, (QVector4D *)&instr.vector);
382 QML_STORE_POINTER(StoreString, &PRIMITIVES.at(instr.value));
383 QML_STORE_POINTER(StoreByteArray, &DATAS.at(instr.value));
384 QML_STORE_POINTER(StoreUrl, &URLS.at(instr.value));
385 QML_STORE_VALUE(StoreTrString, QString,
386 QCoreApplication::translate(DATAS.at(instr.context).constData(),
387 DATAS.at(instr.text).constData(),
388 DATAS.at(instr.comment).constData(),
389 QCoreApplication::UnicodeUTF8,
391 QML_STORE_VALUE(StoreTrIdString, QString, qtTrId(DATAS.at(instr.text).constData(), instr.n));
393 // Store a literal value in a QList
394 QML_STORE_LIST(StoreStringList, QStringList, PRIMITIVES.at(instr.value));
395 QML_STORE_LIST(StoreStringQList, QList<QString>, PRIMITIVES.at(instr.value));
396 QML_STORE_LIST(StoreUrlQList, QList<QUrl>, URLS.at(instr.value));
397 QML_STORE_LIST(StoreDoubleQList, QList<double>, instr.value);
398 QML_STORE_LIST(StoreBoolQList, QList<bool>, instr.value);
399 QML_STORE_LIST(StoreIntegerQList, QList<int>, instr.value);
401 // Store a literal value in a QVariant property
402 QML_STORE_VALUE(StoreVariant, QVariant, variantFromString(PRIMITIVES.at(instr.value)));
403 QML_STORE_VALUE(StoreVariantInteger, QVariant, QVariant(instr.value));
404 QML_STORE_VALUE(StoreVariantDouble, QVariant, QVariant(instr.value));
405 QML_STORE_VALUE(StoreVariantBool, QVariant, QVariant(instr.value));
407 // Store a literal value in a var property.
408 // We deliberately do not use string converters here
409 QML_STORE_VAR(StoreVar, ep->v8engine()->fromVariant(PRIMITIVES.at(instr.value)));
410 QML_STORE_VAR(StoreVarInteger, v8::Integer::New(instr.value));
411 QML_STORE_VAR(StoreVarDouble, v8::Number::New(instr.value));
412 QML_STORE_VAR(StoreVarBool, v8::Boolean::New(instr.value));
415 QML_BEGIN_INSTR(Init)
416 // Ensure that the compiled data has been initialized
417 if (!COMP->isInitialized()) COMP->initialize(engine);
419 QQmlContextData *parentCtxt = CTXT;
420 CTXT = new QQmlContextData;
421 CTXT->isInternal = true;
422 CTXT->url = COMP->url;
423 CTXT->urlString = COMP->name;
424 CTXT->imports = COMP->importCache;
425 CTXT->imports->addref();
426 CTXT->setParent(parentCtxt);
427 if (instr.contextCache != -1)
428 CTXT->setIdPropertyData(COMP->contextCaches.at(instr.contextCache));
429 if (instr.compiledBinding != -1) {
430 const char *v4data = DATAS.at(instr.compiledBinding).constData();
431 CTXT->v4bindings = new QV4Bindings(v4data, CTXT, COMP);
433 if (states.count() == 1) {
435 rootContext->activeVMEData = data;
437 if (states.count() == 1 && !creationContext.isNull()) {
438 // A component that is logically created within another component instance shares the
439 // same instances of script imports. For example:
441 // import QtQuick 2.0
442 // import "test.js" as Test
444 // model: Test.getModel()
445 // delegate: Component {
446 // Text { text: Test.getValue(index); }
450 // Has the same "Test" instance. To implement this, we simply copy the v8 handles into
451 // the inner context. We have to create a fresh persistent handle for each to prevent
452 // double dispose. It is possible we could do this more efficiently using some form of
453 // referencing instead.
454 CTXT->importedScripts = creationContext->importedScripts;
455 for (int ii = 0; ii < CTXT->importedScripts.count(); ++ii)
456 CTXT->importedScripts[ii] = qPersistentNew<v8::Object>(CTXT->importedScripts[ii]);
460 QML_BEGIN_INSTR(DeferInit)
461 QML_END_INSTR(DeferInit)
463 QML_BEGIN_INSTR(Done)
466 if (states.isEmpty())
470 QML_BEGIN_INSTR(CreateQMLObject)
471 const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type);
472 Q_ASSERT(type.component);
474 states.push(State());
476 State *cState = &states[states.count() - 2];
477 State *nState = &states[states.count() - 1];
479 nState->context = cState->context;
480 nState->compiledData = type.component;
481 nState->instructionStream = type.component->bytecode.constData();
483 if (instr.bindingBits != -1) {
484 const QByteArray &bits = cState->compiledData->datas.at(instr.bindingBits);
485 nState->bindingSkipList = QBitField((const quint32*)bits.constData(),
489 nState->bindingSkipList = nState->bindingSkipList.united(cState->bindingSkipList);
491 // As the state in the state stack changed, execution will continue in the new program.
492 QML_END_INSTR(CreateQMLObject)
494 QML_BEGIN_INSTR(CompleteQMLObject)
495 QObject *o = objects.top();
497 QQmlData *ddata = QQmlData::get(o);
501 if (ddata->context) {
502 Q_ASSERT(ddata->context != CTXT);
503 Q_ASSERT(ddata->outerContext);
504 Q_ASSERT(ddata->outerContext != CTXT);
505 QQmlContextData *c = ddata->context;
506 while (c->linkedContext) c = c->linkedContext;
507 c->linkedContext = CTXT;
512 ddata->ownContext = true;
513 } else if (!ddata->context) {
517 ddata->setImplicitDestructible();
518 ddata->outerContext = CTXT;
519 ddata->lineNumber = instr.line;
520 ddata->columnNumber = instr.column;
521 QML_END_INSTR(CompleteQMLObject)
523 QML_BEGIN_INSTR(CreateCppObject)
524 const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type);
529 type.type->create(&o, &memory, sizeof(QQmlData));
530 QQmlData *ddata = new (memory) QQmlData;
531 ddata->ownMemory = false;
532 QObjectPrivate::get(o)->declarativeData = ddata;
534 if (type.typePropertyCache && !ddata->propertyCache) {
535 ddata->propertyCache = type.typePropertyCache;
536 ddata->propertyCache->addref();
540 VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.className), instr.line);
543 if (ddata->context) {
544 Q_ASSERT(ddata->context != CTXT);
545 Q_ASSERT(ddata->outerContext);
546 Q_ASSERT(ddata->outerContext != CTXT);
547 QQmlContextData *c = ddata->context;
548 while (c->linkedContext) c = c->linkedContext;
549 c->linkedContext = CTXT;
554 ddata->ownContext = true;
555 } else if (!ddata->context) {
559 ddata->setImplicitDestructible();
560 ddata->outerContext = CTXT;
561 ddata->lineNumber = instr.line;
562 ddata->columnNumber = instr.column;
564 if (instr.data != -1) {
565 QQmlCustomParser *customParser =
566 TYPES.at(instr.type).type->customParser();
567 customParser->setCustomData(o, DATAS.at(instr.data));
569 if (!objects.isEmpty()) {
570 QObject *parent = objects.top();
571 #if 0 // ### refactor
572 if (o->isWidgetType() && parent->isWidgetType())
573 static_cast<QWidget*>(o)->setParent(static_cast<QWidget*>(parent));
576 QQml_setParent_noEvent(o, parent);
579 QML_END_INSTR(CreateCppObject)
581 QML_BEGIN_INSTR(CreateSimpleObject)
582 QObject *o = (QObject *)operator new(instr.typeSize + sizeof(QQmlData));
583 ::memset(o, 0, instr.typeSize + sizeof(QQmlData));
586 QQmlData *ddata = (QQmlData *)(((const char *)o) + instr.typeSize);
587 const QQmlCompiledData::TypeReference &ref = TYPES.at(instr.type);
588 if (!ddata->propertyCache && ref.typePropertyCache) {
589 ddata->propertyCache = ref.typePropertyCache;
590 ddata->propertyCache->addref();
592 ddata->lineNumber = instr.line;
593 ddata->columnNumber = instr.column;
595 QObjectPrivate::get(o)->declarativeData = ddata;
596 ddata->context = ddata->outerContext = CTXT;
597 ddata->nextContextObject = CTXT->contextObjects;
598 if (ddata->nextContextObject)
599 ddata->nextContextObject->prevContextObject = &ddata->nextContextObject;
600 ddata->prevContextObject = &CTXT->contextObjects;
601 CTXT->contextObjects = ddata;
603 QObject *parent = objects.top();
604 QQml_setParent_noEvent(o, parent);
607 QML_END_INSTR(CreateSimpleObject)
609 QML_BEGIN_INSTR(SetId)
610 QObject *target = objects.top();
611 CTXT->setIdProperty(instr.index, target);
614 QML_BEGIN_INSTR(SetDefault)
615 CTXT->contextObject = objects.top();
616 QML_END_INSTR(SetDefault)
618 QML_BEGIN_INSTR(CreateComponent)
619 QQmlComponent *qcomp =
620 new QQmlComponent(CTXT->engine, COMP, INSTRUCTIONSTREAM - COMP->bytecode.constData(),
621 objects.isEmpty() ? 0 : objects.top());
623 QQmlData *ddata = QQmlData::get(qcomp, true);
626 CTXT->addObject(qcomp);
629 ddata->ownContext = true;
631 ddata->setImplicitDestructible();
632 ddata->outerContext = CTXT;
633 ddata->lineNumber = instr.line;
634 ddata->columnNumber = instr.column;
636 QQmlComponentPrivate::get(qcomp)->creationContext = CTXT;
639 INSTRUCTIONSTREAM += instr.count;
640 QML_END_INSTR(CreateComponent)
642 QML_BEGIN_INSTR(StoreMetaObject)
643 QObject *target = objects.top();
646 const QByteArray &metadata = DATAS.at(instr.data);
647 QFastMetaBuilder::fromData(&mo, 0, metadata);
649 const QQmlVMEMetaData *data =
650 (const QQmlVMEMetaData *)DATAS.at(instr.aliasData).constData();
652 (void)new QQmlVMEMetaObject(target, &mo, data, COMP);
654 if (instr.propertyCache != -1) {
655 QQmlData *ddata = QQmlData::get(target, true);
656 if (ddata->propertyCache) ddata->propertyCache->release();
657 ddata->propertyCache = PROPERTYCACHES.at(instr.propertyCache);
658 ddata->propertyCache->addref();
660 QML_END_INSTR(StoreMetaObject)
662 QML_BEGIN_INSTR(AssignCustomType)
663 QObject *target = objects.top();
664 CLEAN_PROPERTY(target, instr.propertyIndex);
666 const QString &primitive = PRIMITIVES.at(instr.primitive);
667 int type = instr.type;
668 QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type);
669 QVariant v = (*converter)(primitive);
672 target->metaObject()->property(instr.propertyIndex);
673 if (v.isNull() || ((int)prop.type() != type && prop.userType() != type))
674 VME_EXCEPTION(tr("Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name())), instr.line);
676 void *a[] = { (void *)v.data(), 0, &status, &flags };
677 QMetaObject::metacall(target, QMetaObject::WriteProperty,
678 instr.propertyIndex, a);
679 QML_END_INSTR(AssignCustomType)
681 QML_BEGIN_INSTR(AssignSignalObject)
684 QObject *assign = objects.pop();
685 QObject *target = objects.top();
686 int sigIdx = instr.signal;
687 const QString &pr = PRIMITIVES.at(sigIdx);
689 QQmlProperty prop(target, pr);
690 if (prop.type() & QQmlProperty::SignalProperty) {
692 QMetaMethod method = QQmlMetaType::defaultMethod(assign);
693 if (method.signature() == 0)
694 VME_EXCEPTION(tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className())), instr.line);
696 if (!QMetaObject::checkConnectArgs(prop.method().signature(), method.signature()))
697 VME_EXCEPTION(tr("Cannot connect mismatched signal/slot %1 %vs. %2").arg(QString::fromLatin1(method.signature())).arg(QString::fromLatin1(prop.method().signature())), instr.line);
699 QQmlPropertyPrivate::connect(target, prop.index(), assign, method.methodIndex());
702 VME_EXCEPTION(tr("Cannot assign an object to signal property %1").arg(pr), instr.line);
706 QML_END_INSTR(AssignSignalObject)
708 QML_BEGIN_INSTR(StoreSignal)
709 QObject *target = objects.top();
710 QObject *context = objects.at(objects.count() - 1 - instr.context);
712 QMetaMethod signal = target->metaObject()->method(instr.signalIndex);
714 QQmlBoundSignal *bs = new QQmlBoundSignal(target, signal, target);
715 QQmlExpression *expr =
716 new QQmlExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column, *new QQmlExpressionPrivate);
717 bs->setExpression(expr);
719 QML_END_INSTR(StoreSignal)
721 QML_BEGIN_INSTR(StoreImportedScript)
722 CTXT->importedScripts << run(CTXT, SCRIPTS.at(instr.value));
723 QML_END_INSTR(StoreImportedScript)
725 QML_BEGIN_INSTR(StoreScriptString)
726 QObject *target = objects.top();
727 QObject *scope = objects.at(objects.count() - 1 - instr.scope);
729 ss.setContext(CTXT->asQQmlContext());
730 ss.setScopeObject(scope);
731 ss.setScript(PRIMITIVES.at(instr.value));
732 ss.d.data()->bindingId = instr.bindingId;
733 ss.d.data()->lineNumber = instr.line;
734 ss.d.data()->columnNumber = instr.column;
736 void *a[] = { &ss, 0, &status, &flags };
737 QMetaObject::metacall(target, QMetaObject::WriteProperty,
738 instr.propertyIndex, a);
739 QML_END_INSTR(StoreScriptString)
741 QML_BEGIN_INSTR(BeginObject)
742 QObject *target = objects.top();
743 QQmlParserStatus *status = reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(target) + instr.castValue);
744 parserStatus.push(status);
745 #ifdef QML_ENABLE_TRACE
746 Q_ASSERT(QObjectPrivate::get(target)->declarativeData);
747 parserStatusData.push(static_cast<QQmlData *>(QObjectPrivate::get(target)->declarativeData));
749 status->d = &parserStatus.top();
751 status->classBegin();
752 QML_END_INSTR(BeginObject)
754 QML_BEGIN_INSTR(InitV8Bindings)
755 CTXT->v8bindings = new QV8Bindings(&PROGRAMS[instr.programIndex], instr.line, CTXT);
756 QML_END_INSTR(InitV8Bindings)
758 QML_BEGIN_INSTR(StoreBinding)
760 objects.at(objects.count() - 1 - instr.owner);
762 objects.at(objects.count() - 1 - instr.context);
764 if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
765 QML_NEXT_INSTR(StoreBinding);
767 QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true,
768 context, CTXT, COMP->name, instr.line,
770 bindValues.push(bind);
771 bind->m_mePtr = &bindValues.top();
772 bind->setTarget(target, instr.property, CTXT);
774 typedef QQmlPropertyPrivate QDPP;
775 Q_ASSERT(bind->propertyIndex() == QDPP::bindingIndex(instr.property));
776 Q_ASSERT(bind->object() == target);
779 QML_END_INSTR(StoreBinding)
781 QML_BEGIN_INSTR(StoreBindingOnAlias)
783 objects.at(objects.count() - 1 - instr.owner);
785 objects.at(objects.count() - 1 - instr.context);
787 if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
788 QML_NEXT_INSTR(StoreBindingOnAlias);
790 QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true,
791 context, CTXT, COMP->name, instr.line,
793 bindValues.push(bind);
794 bind->m_mePtr = &bindValues.top();
795 bind->setTarget(target, instr.property, CTXT);
797 QQmlAbstractBinding *old =
798 QQmlPropertyPrivate::setBindingNoEnable(target, instr.property.coreIndex,
799 instr.property.getValueTypeCoreIndex(),
801 if (old) { old->destroy(); }
802 QML_END_INSTR(StoreBindingOnAlias)
804 QML_BEGIN_INSTR(StoreV4Binding)
806 objects.at(objects.count() - 1 - instr.owner);
808 objects.at(objects.count() - 1 - instr.context);
810 int property = instr.property;
811 if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF))
812 QML_NEXT_INSTR(StoreV4Binding);
814 QQmlAbstractBinding *binding =
815 CTXT->v4bindings->configBinding(instr.value, target, scope, property,
816 instr.line, instr.column);
817 bindValues.push(binding);
818 binding->m_mePtr = &bindValues.top();
820 Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF));
821 Q_ASSERT(binding->object() == target);
823 binding->addToObject();
824 QML_END_INSTR(StoreV4Binding)
826 QML_BEGIN_INSTR(StoreV8Binding)
828 objects.at(objects.count() - 1 - instr.owner);
830 objects.at(objects.count() - 1 - instr.context);
832 if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
833 QML_NEXT_INSTR(StoreV8Binding);
835 QQmlAbstractBinding *binding = CTXT->v8bindings->configBinding(target, scope,
838 bindValues.push(binding);
839 binding->m_mePtr = &bindValues.top();
841 typedef QQmlPropertyPrivate QDPP;
842 Q_ASSERT(binding->propertyIndex() == QDPP::bindingIndex(instr.property));
843 Q_ASSERT(binding->object() == target);
845 binding->addToObject();
847 QML_END_INSTR(StoreV8Binding)
849 QML_BEGIN_INSTR(StoreValueSource)
850 QObject *obj = objects.pop();
851 QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(obj) + instr.castValue);
852 QObject *target = objects.at(objects.count() - 1 - instr.owner);
854 obj->setParent(target);
855 vs->setTarget(QQmlPropertyPrivate::restore(target, instr.property, CTXT));
856 QML_END_INSTR(StoreValueSource)
858 QML_BEGIN_INSTR(StoreValueInterceptor)
859 QObject *obj = objects.pop();
860 QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(obj) + instr.castValue);
861 QObject *target = objects.at(objects.count() - 1 - instr.owner);
863 QQmlPropertyPrivate::restore(target, instr.property, CTXT);
864 obj->setParent(target);
866 QQmlVMEMetaObject *mo = static_cast<QQmlVMEMetaObject *>((QMetaObject*)target->metaObject());
867 mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi);
868 QML_END_INSTR(StoreValueInterceptor)
870 QML_BEGIN_INSTR(StoreObjectQList)
871 QObject *assign = objects.pop();
873 const List &list = lists.top();
874 list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, assign);
875 QML_END_INSTR(StoreObjectQList)
877 QML_BEGIN_INSTR(AssignObjectList)
878 // This is only used for assigning interfaces
879 QObject *assign = objects.pop();
880 const List &list = lists.top();
882 int type = list.type;
886 const char *iid = QQmlMetaType::interfaceIId(type);
888 ptr = assign->qt_metacast(iid);
890 VME_EXCEPTION(tr("Cannot assign object to list"), instr.line);
893 list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, ptr);
894 QML_END_INSTR(AssignObjectList)
896 QML_BEGIN_INSTR(StoreInterface)
897 QObject *assign = objects.pop();
898 QObject *target = objects.top();
899 CLEAN_PROPERTY(target, instr.propertyIndex);
901 int coreIdx = instr.propertyIndex;
902 QMetaProperty prop = target->metaObject()->property(coreIdx);
903 int t = prop.userType();
904 const char *iid = QQmlMetaType::interfaceIId(t);
907 void *ptr = assign->qt_metacast(iid);
909 void *a[] = { &ptr, 0, &status, &flags };
910 QMetaObject::metacall(target,
911 QMetaObject::WriteProperty,
918 VME_EXCEPTION(tr("Cannot assign object to interface property"), instr.line);
919 QML_END_INSTR(StoreInterface)
921 QML_BEGIN_INSTR(FetchAttached)
922 QObject *target = objects.top();
924 QObject *qmlObject = qmlAttachedPropertiesObjectById(instr.id, target);
927 VME_EXCEPTION(tr("Unable to create attached object"), instr.line);
929 objects.push(qmlObject);
930 QML_END_INSTR(FetchAttached)
932 QML_BEGIN_INSTR(FetchQList)
933 QObject *target = objects.top();
935 lists.push(List(instr.type));
938 a[0] = (void *)&(lists.top().qListProperty);
939 QMetaObject::metacall(target, QMetaObject::ReadProperty,
941 QML_END_INSTR(FetchQList)
943 QML_BEGIN_INSTR(FetchObject)
944 QObject *target = objects.top();
947 // NOTE: This assumes a cast to QObject does not alter the
951 QMetaObject::metacall(target, QMetaObject::ReadProperty,
955 VME_EXCEPTION(tr("Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line);
958 QML_END_INSTR(FetchObject)
960 QML_BEGIN_INSTR(PopQList)
962 QML_END_INSTR(PopQList)
964 QML_BEGIN_INSTR(Defer)
965 if (instr.deferCount) {
966 QObject *target = objects.top();
968 QQmlData::get(target, true);
970 data->deferredComponent = COMP;
971 data->deferredIdx = INSTRUCTIONSTREAM - COMP->bytecode.constData();
972 INSTRUCTIONSTREAM += instr.deferCount;
976 QML_BEGIN_INSTR(PopFetchedObject)
978 QML_END_INSTR(PopFetchedObject)
980 QML_BEGIN_INSTR(FetchValueType)
981 QObject *target = objects.top();
983 if (instr.bindingSkipList != 0) {
984 // Possibly need to clear bindings
985 QQmlData *targetData = QQmlData::get(target);
987 QQmlAbstractBinding *binding =
988 QQmlPropertyPrivate::binding(target, instr.property, -1);
990 if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) {
991 QQmlPropertyPrivate::setBinding(target, instr.property, -1, 0);
993 } else if (binding) {
994 QQmlValueTypeProxyBinding *proxy =
995 static_cast<QQmlValueTypeProxyBinding *>(binding);
996 proxy->removeBindings(instr.bindingSkipList);
1001 QQmlValueType *valueHandler = ep->valueTypes[instr.type];
1002 valueHandler->read(target, instr.property);
1003 objects.push(valueHandler);
1004 QML_END_INSTR(FetchValueType)
1006 QML_BEGIN_INSTR(PopValueType)
1007 QQmlValueType *valueHandler =
1008 static_cast<QQmlValueType *>(objects.pop());
1009 QObject *target = objects.top();
1010 valueHandler->write(target, instr.property, QQmlPropertyPrivate::BypassInterceptor);
1011 QML_END_INSTR(PopValueType)
1013 #ifdef QML_THREADED_VME_INTERPRETER
1017 qFatal("QQmlCompiledData: Internal error - unknown instruction %d", genericInstr->common.instructionType);
1024 Q_ASSERT(!states.isEmpty());
1025 Q_ASSERT(!errors->isEmpty());
1032 Q_ASSERT(objects.count() == 1);
1034 QObject *rv = objects.top();
1036 objects.deallocate();
1043 void QQmlVME::reset()
1045 Q_ASSERT(!states.isEmpty() || objects.isEmpty());
1047 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
1049 if (!objects.isEmpty() && !(states.at(0).flags & State::Deferred))
1050 delete objects.at(0);
1052 if (!rootContext.isNull())
1053 rootContext->activeVMEData = 0;
1055 // Remove the QQmlParserStatus and QQmlAbstractBinding back pointers
1056 blank(parserStatus);
1059 while (componentAttached) {
1060 QQmlComponentAttached *a = componentAttached;
1065 objects.deallocate();
1067 bindValues.deallocate();
1068 parserStatus.deallocate();
1069 #ifdef QML_ENABLE_TRACE
1070 parserStatusData.deallocate();
1072 finalizeCallbacks.clear();
1075 creationContext = 0;
1078 // Must be called with a handle scope and context
1079 void QQmlScriptData::initialize(QQmlEngine *engine)
1081 Q_ASSERT(m_program.IsEmpty());
1083 Q_ASSERT(!hasEngine());
1085 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1086 QV8Engine *v8engine = ep->v8engine();
1088 // If compilation throws an error, a surrounding v8::TryCatch will record it.
1089 v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_programSource.constData(),
1090 m_programSource.length(), urlString, 1);
1091 if (program.IsEmpty())
1094 m_program = qPersistentNew<v8::Script>(program);
1095 m_programSource.clear(); // We don't need this anymore
1097 addToEngine(engine);
1102 v8::Persistent<v8::Object> QQmlVME::run(QQmlContextData *parentCtxt, QQmlScriptData *script)
1104 if (script->m_loaded)
1105 return qPersistentNew<v8::Object>(script->m_value);
1107 Q_ASSERT(parentCtxt && parentCtxt->engine);
1108 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
1109 QV8Engine *v8engine = ep->v8engine();
1111 bool shared = script->pragmas & QQmlScript::Object::ScriptBlock::Shared;
1113 QQmlContextData *effectiveCtxt = parentCtxt;
1117 // Create the script context if required
1118 QQmlContextData *ctxt = new QQmlContextData;
1119 ctxt->isInternal = true;
1120 ctxt->isJSContext = true;
1122 ctxt->isPragmaLibraryContext = true;
1124 ctxt->isPragmaLibraryContext = parentCtxt->isPragmaLibraryContext;
1125 ctxt->url = script->url;
1126 ctxt->urlString = script->urlString;
1128 // For backward compatibility, if there are no imports, we need to use the
1129 // imports from the parent context. See QTBUG-17518.
1130 if (!script->importCache->isEmpty()) {
1131 ctxt->imports = script->importCache;
1132 } else if (effectiveCtxt) {
1133 ctxt->imports = effectiveCtxt->imports;
1134 ctxt->importedScripts = effectiveCtxt->importedScripts;
1135 for (int ii = 0; ii < ctxt->importedScripts.count(); ++ii)
1136 ctxt->importedScripts[ii] = qPersistentNew<v8::Object>(ctxt->importedScripts[ii]);
1139 if (ctxt->imports) {
1140 ctxt->imports->addref();
1143 if (effectiveCtxt) {
1144 ctxt->setParent(effectiveCtxt, true);
1146 ctxt->engine = parentCtxt->engine; // Fix for QTBUG-21620
1149 for (int ii = 0; ii < script->scripts.count(); ++ii) {
1150 ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData());
1153 v8::HandleScope handle_scope;
1154 v8::Context::Scope scope(v8engine->context());
1156 v8::TryCatch try_catch;
1157 if (!script->isInitialized())
1158 script->initialize(parentCtxt->engine);
1160 v8::Local<v8::Object> qmlglobal = v8engine->qmlScope(ctxt, 0);
1162 if (!script->m_program.IsEmpty()) {
1163 script->m_program->Run(qmlglobal);
1165 // Compilation failed.
1166 Q_ASSERT(try_catch.HasCaught());
1169 v8::Persistent<v8::Object> rv;
1171 if (try_catch.HasCaught()) {
1172 v8::Local<v8::Message> message = try_catch.Message();
1173 if (!message.IsEmpty()) {
1175 QQmlExpressionPrivate::exceptionToError(message, error);
1180 rv = qPersistentNew<v8::Object>(qmlglobal);
1182 script->m_value = qPersistentNew<v8::Object>(qmlglobal);
1183 script->m_loaded = true;
1189 #ifdef QML_THREADED_VME_INTERPRETER
1190 void **QQmlVME::instructionJumpTable()
1192 static void **jumpTable = 0;
1195 QQmlVME::Interrupt i;
1196 dummy.run(0, i, &jumpTable);
1202 QQmlContextData *QQmlVME::complete(const Interrupt &interrupt)
1205 (bindValues.isEmpty() &&
1206 parserStatus.isEmpty() &&
1207 componentAttached == 0 &&
1208 rootContext.isNull() &&
1209 finalizeCallbacks.isEmpty()));
1214 QQmlTrace trace("VME Complete");
1215 #ifdef QML_ENABLE_TRACE
1216 trace.addDetail("URL", rootComponent->url);
1219 ActiveVMERestorer restore(this, QQmlEnginePrivate::get(engine));
1220 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
1223 QQmlTrace trace("VME Binding Enable");
1224 trace.event("begin binding eval");
1225 while (!bindValues.isEmpty()) {
1226 QQmlAbstractBinding *b = bindValues.pop();
1230 b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
1231 QQmlPropertyPrivate::DontRemoveBinding);
1234 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1237 bindValues.deallocate();
1241 QQmlTrace trace("VME Component Complete");
1242 while (!parserStatus.isEmpty()) {
1243 QQmlParserStatus *status = parserStatus.pop();
1244 #ifdef QML_ENABLE_TRACE
1245 QQmlData *data = parserStatusData.pop();
1248 if (status && status->d) {
1250 #ifdef QML_ENABLE_TRACE
1251 QQmlTrace trace("Component complete");
1252 trace.addDetail("URL", data->outerContext->url);
1253 trace.addDetail("Line", data->lineNumber);
1255 status->componentComplete();
1258 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1261 parserStatus.deallocate();
1265 QQmlTrace trace("VME Finalize Callbacks");
1266 for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) {
1267 QQmlEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii);
1268 QObject *obj = callback.first;
1270 void *args[] = { 0 };
1271 QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args);
1273 if (watcher.hasRecursed())
1276 finalizeCallbacks.clear();
1280 QQmlTrace trace("VME Component.onCompleted Callbacks");
1281 while (componentAttached) {
1282 QQmlComponentAttached *a = componentAttached;
1284 QQmlData *d = QQmlData::get(a->parent());
1286 Q_ASSERT(d->context);
1287 a->add(&d->context->componentAttached);
1288 emit a->completed();
1290 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1295 QQmlContextData *rv = rootContext;
1299 if (rv) rv->activeVMEData = data;
1304 void QQmlVME::blank(QFiniteStack<QQmlAbstractBinding *> &bs)
1306 for (int ii = 0; ii < bs.count(); ++ii) {
1307 QQmlAbstractBinding *b = bs.at(ii);
1308 if (b) b->m_mePtr = 0;
1312 void QQmlVME::blank(QFiniteStack<QQmlParserStatus *> &pss)
1314 for (int ii = 0; ii < pss.count(); ++ii) {
1315 QQmlParserStatus *ps = pss.at(ii);
1320 QQmlVMEGuard::QQmlVMEGuard()
1321 : m_objectCount(0), m_objects(0), m_contextCount(0), m_contexts(0)
1325 QQmlVMEGuard::~QQmlVMEGuard()
1330 void QQmlVMEGuard::guard(QQmlVME *vme)
1334 m_objectCount = vme->objects.count();
1335 m_objects = new QQmlGuard<QObject>[m_objectCount];
1336 for (int ii = 0; ii < m_objectCount; ++ii)
1337 m_objects[ii] = vme->objects[ii];
1339 m_contextCount = (vme->rootContext.isNull()?0:1) + vme->states.count();
1340 m_contexts = new QQmlGuardedContextData[m_contextCount];
1341 for (int ii = 0; ii < vme->states.count(); ++ii)
1342 m_contexts[ii] = vme->states.at(ii).context;
1343 if (!vme->rootContext.isNull())
1344 m_contexts[m_contextCount - 1] = vme->rootContext.contextData();
1347 void QQmlVMEGuard::clear()
1349 delete [] m_objects;
1350 delete [] m_contexts;
1358 bool QQmlVMEGuard::isOK() const
1360 for (int ii = 0; ii < m_objectCount; ++ii)
1361 if (m_objects[ii].isNull())
1364 for (int ii = 0; ii < m_contextCount; ++ii)
1365 if (m_contexts[ii].isNull())