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"
74 #include <QtCore/qdebug.h>
75 #include <QtCore/qvarlengtharray.h>
76 #include <QtCore/qcoreapplication.h>
77 #include <QtCore/qdatetime.h>
78 #include <QtCore/qvarlengtharray.h>
79 #include <QtQml/qjsvalue.h>
83 using namespace QQmlVMETypes;
85 #define VME_EXCEPTION(desc, line) \
88 error.setDescription(desc.trimmed()); \
89 error.setLine(line); \
90 error.setUrl(COMP->url); \
95 void QQmlVME::init(QQmlContextData *ctxt, QQmlCompiledData *comp, int start,
96 QQmlContextData *creation)
104 creationContext = creation;
108 initState.context = ctxt;
109 initState.compiledData = comp;
110 initState.instructionStream = comp->bytecode.constData() + start;
111 states.push(initState);
113 typedef QQmlInstruction I;
114 I *i = (I *)initState.instructionStream;
116 Q_ASSERT(comp->instructionType(i) == I::Init);
118 objects.allocate(i->init.objectStackSize);
119 lists.allocate(i->init.listStackSize);
120 bindValues.allocate(i->init.bindingsSize);
121 parserStatus.allocate(i->init.parserStatusSize);
123 #ifdef QML_ENABLE_TRACE
124 parserStatusData.allocate(i->init.parserStatusSize);
125 rootComponent = comp;
129 engine = ctxt->engine;
132 bool QQmlVME::initDeferred(QObject *object)
134 QQmlData *data = QQmlData::get(object);
136 if (!data || !data->context || !data->deferredComponent)
139 QQmlContextData *ctxt = data->context;
140 QQmlCompiledData *comp = data->deferredComponent;
141 int start = data->deferredIdx;
144 initState.flags = State::Deferred;
145 initState.context = ctxt;
146 initState.compiledData = comp;
147 initState.instructionStream = comp->bytecode.constData() + start;
148 states.push(initState);
150 typedef QQmlInstruction I;
151 I *i = (I *)initState.instructionStream;
153 Q_ASSERT(comp->instructionType(i) == I::DeferInit);
155 objects.allocate(i->deferInit.objectStackSize);
156 lists.allocate(i->deferInit.listStackSize);
157 bindValues.allocate(i->deferInit.bindingsSize);
158 parserStatus.allocate(i->deferInit.parserStatusSize);
160 objects.push(object);
162 #ifdef QML_ENABLE_TRACE
163 parserStatusData.allocate(i->deferInit.parserStatusSize);
164 rootComponent = comp;
168 engine = ctxt->engine;
174 struct ActiveVMERestorer
176 ActiveVMERestorer(QQmlVME *me, QQmlEnginePrivate *ep)
177 : ep(ep), oldVME(ep->activeVME) { ep->activeVME = me; }
178 ~ActiveVMERestorer() { ep->activeVME = oldVME; }
180 QQmlEnginePrivate *ep;
185 QObject *QQmlVME::execute(QList<QQmlError> *errors, const Interrupt &interrupt)
187 Q_ASSERT(states.count() >= 1);
189 #ifdef QML_ENABLE_TRACE
190 QQmlTrace trace("VME Execute");
191 trace.addDetail("URL", rootComponent->url);
194 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(states.at(0).context->engine);
196 ActiveVMERestorer restore(this, ep);
198 QObject *rv = run(errors, interrupt);
203 inline bool fastHasBinding(QObject *o, int index)
205 QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(o)->declarativeData);
207 index &= 0xFFFFFF; // To handle value types
209 return ddata && (ddata->bindingBitsSize > index) &&
210 (ddata->bindingBits[index / 32] & (1 << (index % 32)));
213 static void removeBindingOnProperty(QObject *o, int index)
215 int coreIndex = index & 0xFFFFFF;
216 int valueTypeIndex = index & 0xFF000000;
217 if (!valueTypeIndex) valueTypeIndex = -1;
219 QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0);
220 if (binding) binding->destroy();
223 static QVariant variantFromString(const QString &string)
225 return QQmlStringConverters::variantFromString(string);
228 // XXX we probably need some form of "work count" here to prevent us checking this
229 // for every instruction.
230 #define QML_BEGIN_INSTR_COMMON(I) { \
231 const QQmlInstructionMeta<(int)QQmlInstruction::I>::DataType &instr = QQmlInstructionMeta<(int)QQmlInstruction::I>::data(*genericInstr); \
232 INSTRUCTIONSTREAM += QQmlInstructionMeta<(int)QQmlInstruction::I>::Size; \
235 #ifdef QML_THREADED_VME_INTERPRETER
236 # define QML_BEGIN_INSTR(I) op_##I: \
237 QML_BEGIN_INSTR_COMMON(I)
239 # define QML_NEXT_INSTR(I) { \
240 if (watcher.hasRecursed()) return 0; \
241 genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \
242 goto *genericInstr->common.code; \
245 # define QML_END_INSTR(I) } \
246 if (watcher.hasRecursed()) return 0; \
247 genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM); \
248 if (interrupt.shouldInterrupt()) return 0; \
249 goto *genericInstr->common.code;
252 # define QML_BEGIN_INSTR(I) \
253 case QQmlInstruction::I: \
254 QML_BEGIN_INSTR_COMMON(I)
256 # define QML_NEXT_INSTR(I) { \
257 if (watcher.hasRecursed()) return 0; \
261 # define QML_END_INSTR(I) \
262 if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return 0; \
266 #define QML_STORE_VALUE(name, cpptype, value) \
267 QML_BEGIN_INSTR(name) \
269 void *a[] = { (void *)&v, 0, &status, &flags }; \
270 QObject *target = objects.top(); \
271 CLEAN_PROPERTY(target, instr.propertyIndex); \
272 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
275 #define QML_STORE_PROVIDER_VALUE(name, type, value) \
276 QML_BEGIN_INSTR(name) \
277 struct { void *data[4]; } buffer; \
278 if (QQml_valueTypeProvider()->storeValueType(type, &value, &buffer, sizeof(buffer))) { \
279 void *a[] = { reinterpret_cast<void *>(&buffer), 0, &status, &flags }; \
280 QObject *target = objects.top(); \
281 CLEAN_PROPERTY(target, instr.propertyIndex); \
282 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
286 #define QML_STORE_LIST(name, cpptype, value) \
287 QML_BEGIN_INSTR(name) \
290 void *a[] = { (void *)&v, 0, &status, &flags }; \
291 QObject *target = objects.top(); \
292 CLEAN_PROPERTY(target, instr.propertyIndex); \
293 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
296 #define QML_STORE_VAR(name, value) \
297 QML_BEGIN_INSTR(name) \
298 v8::Handle<v8::Value> v8value = value; \
299 QObject *target = objects.top(); \
300 CLEAN_PROPERTY(target, instr.propertyIndex); \
301 QMetaObject *mo = const_cast<QMetaObject *>(target->metaObject()); \
302 QQmlVMEMetaObject *vmemo = static_cast<QQmlVMEMetaObject *>(mo); \
303 vmemo->setVMEProperty(instr.propertyIndex, v8value); \
306 #define QML_STORE_POINTER(name, value) \
307 QML_BEGIN_INSTR(name) \
308 void *a[] = { (void *)value, 0, &status, &flags }; \
309 QObject *target = objects.top(); \
310 CLEAN_PROPERTY(target, instr.propertyIndex); \
311 QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); \
314 #define CLEAN_PROPERTY(o, index) \
315 if (fastHasBinding(o, index)) \
316 removeBindingOnProperty(o, index)
318 QObject *QQmlVME::run(QList<QQmlError> *errors,
319 const Interrupt &interrupt
320 #ifdef QML_THREADED_VME_INTERPRETER
321 , void ***storeJumpTable
325 #ifdef QML_THREADED_VME_INTERPRETER
326 if (storeJumpTable) {
327 #define QML_INSTR_ADDR(I, FMT) &&op_##I,
328 static void *jumpTable[] = {
329 FOR_EACH_QML_INSTR(QML_INSTR_ADDR)
331 #undef QML_INSTR_ADDR
332 *storeJumpTable = jumpTable;
336 Q_ASSERT(errors->isEmpty());
337 Q_ASSERT(states.count() >= 1);
339 QQmlEngine *engine = states.at(0).context->engine;
340 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
342 // Need a v8 handle scope and execution context for StoreVar instructions.
343 v8::HandleScope handleScope;
344 v8::Context::Scope contextScope(ep->v8engine()->context());
346 int status = -1; // needed for dbus
347 QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::BypassInterceptor |
348 QQmlPropertyPrivate::RemoveBindingOnAliasWrite;
350 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
352 #define COMP states.top().compiledData
353 #define CTXT states.top().context
354 #define INSTRUCTIONSTREAM states.top().instructionStream
355 #define BINDINGSKIPLIST states.top().bindingSkipList
357 #define TYPES COMP->types
358 #define PRIMITIVES COMP->primitives
359 #define DATAS COMP->datas
360 #define PROGRAMS COMP->programs
361 #define PROPERTYCACHES COMP->propertyCaches
362 #define SCRIPTS COMP->scripts
363 #define URLS COMP->urls
365 #ifdef QML_THREADED_VME_INTERPRETER
366 const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM);
367 goto *genericInstr->common.code;
370 const QQmlInstruction *genericInstr = reinterpret_cast<const QQmlInstruction *>(INSTRUCTIONSTREAM);
372 switch (genericInstr->common.instructionType) {
375 // Store a created object in a property. These all pop from the objects stack.
376 QML_STORE_VALUE(StoreObject, QObject *, objects.pop());
377 QML_STORE_VALUE(StoreVariantObject, QVariant, QVariant::fromValue(objects.pop()));
378 QML_STORE_VAR(StoreVarObject, ep->v8engine()->newQObject(objects.pop()));
380 // Store a literal value in a corresponding property
381 QML_STORE_VALUE(StoreFloat, float, instr.value);
382 QML_STORE_VALUE(StoreDouble, double, instr.value);
383 QML_STORE_VALUE(StoreBool, bool, instr.value);
384 QML_STORE_VALUE(StoreInteger, int, instr.value);
385 QML_STORE_PROVIDER_VALUE(StoreColor, QMetaType::QColor, instr.value);
386 QML_STORE_VALUE(StoreDate, QDate, QDate::fromJulianDay(instr.value));
387 QML_STORE_VALUE(StoreDateTime, QDateTime,
388 QDateTime(QDate::fromJulianDay(instr.date), *(QTime *)&instr.time));
389 QML_STORE_POINTER(StoreTime, (QTime *)&instr.time);
390 QML_STORE_POINTER(StorePoint, (QPoint *)&instr.point);
391 QML_STORE_POINTER(StorePointF, (QPointF *)&instr.point);
392 QML_STORE_POINTER(StoreSize, (QSize *)&instr.size);
393 QML_STORE_POINTER(StoreSizeF, (QSizeF *)&instr.size);
394 QML_STORE_POINTER(StoreRect, (QRect *)&instr.rect);
395 QML_STORE_POINTER(StoreRectF, (QRectF *)&instr.rect);
396 QML_STORE_PROVIDER_VALUE(StoreVector3D, QMetaType::QVector3D, instr.vector);
397 QML_STORE_PROVIDER_VALUE(StoreVector4D, QMetaType::QVector4D, instr.vector);
398 QML_STORE_POINTER(StoreString, &PRIMITIVES.at(instr.value));
399 QML_STORE_POINTER(StoreByteArray, &DATAS.at(instr.value));
400 QML_STORE_POINTER(StoreUrl, &URLS.at(instr.value));
401 QML_STORE_VALUE(StoreTrString, QString,
402 QCoreApplication::translate(DATAS.at(instr.context).constData(),
403 DATAS.at(instr.text).constData(),
404 DATAS.at(instr.comment).constData(),
405 QCoreApplication::UnicodeUTF8,
407 QML_STORE_VALUE(StoreTrIdString, QString, qtTrId(DATAS.at(instr.text).constData(), instr.n));
409 // Store a literal value in a QList
410 QML_STORE_LIST(StoreStringList, QStringList, PRIMITIVES.at(instr.value));
411 QML_STORE_LIST(StoreStringQList, QList<QString>, PRIMITIVES.at(instr.value));
412 QML_STORE_LIST(StoreUrlQList, QList<QUrl>, URLS.at(instr.value));
413 QML_STORE_LIST(StoreDoubleQList, QList<double>, instr.value);
414 QML_STORE_LIST(StoreBoolQList, QList<bool>, instr.value);
415 QML_STORE_LIST(StoreIntegerQList, QList<int>, instr.value);
417 // Store a literal value in a QVariant property
418 QML_STORE_VALUE(StoreVariant, QVariant, variantFromString(PRIMITIVES.at(instr.value)));
419 QML_STORE_VALUE(StoreVariantInteger, QVariant, QVariant(instr.value));
420 QML_STORE_VALUE(StoreVariantDouble, QVariant, QVariant(instr.value));
421 QML_STORE_VALUE(StoreVariantBool, QVariant, QVariant(instr.value));
423 // Store a literal value in a var property.
424 // We deliberately do not use string converters here
425 QML_STORE_VAR(StoreVar, ep->v8engine()->fromVariant(PRIMITIVES.at(instr.value)));
426 QML_STORE_VAR(StoreVarInteger, v8::Integer::New(instr.value));
427 QML_STORE_VAR(StoreVarDouble, v8::Number::New(instr.value));
428 QML_STORE_VAR(StoreVarBool, v8::Boolean::New(instr.value));
431 QML_BEGIN_INSTR(Init)
432 // Ensure that the compiled data has been initialized
433 if (!COMP->isInitialized()) COMP->initialize(engine);
435 QQmlContextData *parentCtxt = CTXT;
436 CTXT = new QQmlContextData;
437 CTXT->isInternal = true;
438 CTXT->url = COMP->url;
439 CTXT->urlString = COMP->name;
440 CTXT->imports = COMP->importCache;
441 CTXT->imports->addref();
442 CTXT->setParent(parentCtxt);
443 if (instr.contextCache != -1)
444 CTXT->setIdPropertyData(COMP->contextCaches.at(instr.contextCache));
445 if (instr.compiledBinding != -1) {
446 const char *v4data = DATAS.at(instr.compiledBinding).constData();
447 CTXT->v4bindings = new QV4Bindings(v4data, CTXT, COMP);
449 if (states.count() == 1) {
451 rootContext->activeVMEData = data;
452 rootContext->isRootObjectInCreation = true;
454 if (states.count() == 1 && !creationContext.isNull()) {
455 // A component that is logically created within another component instance shares the
456 // same instances of script imports. For example:
458 // import QtQuick 2.0
459 // import "test.js" as Test
461 // model: Test.getModel()
462 // delegate: Component {
463 // Text { text: Test.getValue(index); }
467 // Has the same "Test" instance. To implement this, we simply copy the v8 handles into
468 // the inner context. We have to create a fresh persistent handle for each to prevent
469 // double dispose. It is possible we could do this more efficiently using some form of
470 // referencing instead.
471 CTXT->importedScripts = creationContext->importedScripts;
472 for (int ii = 0; ii < CTXT->importedScripts.count(); ++ii)
473 CTXT->importedScripts[ii] = qPersistentNew<v8::Object>(CTXT->importedScripts[ii]);
477 QML_BEGIN_INSTR(DeferInit)
478 QML_END_INSTR(DeferInit)
480 QML_BEGIN_INSTR(Done)
483 if (states.isEmpty())
487 QML_BEGIN_INSTR(CreateQMLObject)
488 const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type);
489 Q_ASSERT(type.component);
491 states.push(State());
493 State *cState = &states[states.count() - 2];
494 State *nState = &states[states.count() - 1];
496 nState->context = cState->context;
497 nState->compiledData = type.component;
498 nState->instructionStream = type.component->bytecode.constData();
500 if (instr.bindingBits != -1) {
501 const QByteArray &bits = cState->compiledData->datas.at(instr.bindingBits);
502 nState->bindingSkipList = QBitField((const quint32*)bits.constData(),
506 nState->bindingSkipList = nState->bindingSkipList.united(cState->bindingSkipList);
508 // As the state in the state stack changed, execution will continue in the new program.
509 QML_END_INSTR(CreateQMLObject)
511 QML_BEGIN_INSTR(CompleteQMLObject)
512 QObject *o = objects.top();
515 QQmlData *ddata = QQmlData::get(o);
519 if (ddata->context) {
520 Q_ASSERT(ddata->context != CTXT);
521 Q_ASSERT(ddata->outerContext);
522 Q_ASSERT(ddata->outerContext != CTXT);
523 QQmlContextData *c = ddata->context;
524 while (c->linkedContext) c = c->linkedContext;
525 c->linkedContext = CTXT;
530 ddata->ownContext = true;
531 } else if (!ddata->context) {
535 ddata->setImplicitDestructible();
536 ddata->outerContext = CTXT;
537 ddata->lineNumber = instr.line;
538 ddata->columnNumber = instr.column;
539 QML_END_INSTR(CompleteQMLObject)
541 QML_BEGIN_INSTR(CreateCppObject)
542 const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type);
547 type.type->create(&o, &memory, sizeof(QQmlData));
548 QQmlData *ddata = new (memory) QQmlData;
549 ddata->ownMemory = false;
550 QObjectPrivate::get(o)->declarativeData = ddata;
552 if (rootContext && rootContext->isRootObjectInCreation) {
553 ddata->rootObjectInCreation = true;
554 rootContext->isRootObjectInCreation = false;
557 if (type.typePropertyCache && !ddata->propertyCache) {
558 ddata->propertyCache = type.typePropertyCache;
559 ddata->propertyCache->addref();
563 VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.type->elementName()),
567 if (ddata->context) {
568 Q_ASSERT(ddata->context != CTXT);
569 Q_ASSERT(ddata->outerContext);
570 Q_ASSERT(ddata->outerContext != CTXT);
571 QQmlContextData *c = ddata->context;
572 while (c->linkedContext) c = c->linkedContext;
573 c->linkedContext = CTXT;
578 ddata->ownContext = true;
579 } else if (!ddata->context) {
583 ddata->setImplicitDestructible();
584 ddata->outerContext = CTXT;
585 ddata->lineNumber = instr.line;
586 ddata->columnNumber = instr.column;
588 if (instr.data != -1) {
589 QQmlCustomParser *customParser =
590 TYPES.at(instr.type).type->customParser();
591 customParser->setCustomData(o, DATAS.at(instr.data));
593 if (!objects.isEmpty()) {
594 QObject *parent = objects.top();
595 #if 0 // ### refactor
596 if (o->isWidgetType() && parent->isWidgetType())
597 static_cast<QWidget*>(o)->setParent(static_cast<QWidget*>(parent));
600 QQml_setParent_noEvent(o, parent);
603 QML_END_INSTR(CreateCppObject)
605 QML_BEGIN_INSTR(CreateSimpleObject)
606 QObject *o = (QObject *)operator new(instr.typeSize + sizeof(QQmlData));
607 ::memset(o, 0, instr.typeSize + sizeof(QQmlData));
610 QQmlData *ddata = (QQmlData *)(((const char *)o) + instr.typeSize);
611 const QQmlCompiledData::TypeReference &ref = TYPES.at(instr.type);
612 if (!ddata->propertyCache && ref.typePropertyCache) {
613 ddata->propertyCache = ref.typePropertyCache;
614 ddata->propertyCache->addref();
616 ddata->lineNumber = instr.line;
617 ddata->columnNumber = instr.column;
619 QObjectPrivate::get(o)->declarativeData = ddata;
620 ddata->context = ddata->outerContext = CTXT;
621 ddata->nextContextObject = CTXT->contextObjects;
622 if (ddata->nextContextObject)
623 ddata->nextContextObject->prevContextObject = &ddata->nextContextObject;
624 ddata->prevContextObject = &CTXT->contextObjects;
625 CTXT->contextObjects = ddata;
627 QObject *parent = objects.top();
628 QQml_setParent_noEvent(o, parent);
631 QML_END_INSTR(CreateSimpleObject)
633 QML_BEGIN_INSTR(SetId)
634 QObject *target = objects.top();
635 CTXT->setIdProperty(instr.index, target);
638 QML_BEGIN_INSTR(SetDefault)
639 CTXT->contextObject = objects.top();
640 QML_END_INSTR(SetDefault)
642 QML_BEGIN_INSTR(CreateComponent)
643 QQmlComponent *qcomp =
644 new QQmlComponent(CTXT->engine, COMP, INSTRUCTIONSTREAM - COMP->bytecode.constData(),
645 objects.isEmpty() ? 0 : objects.top());
647 QQmlData *ddata = QQmlData::get(qcomp, true);
650 CTXT->addObject(qcomp);
653 ddata->ownContext = true;
655 ddata->setImplicitDestructible();
656 ddata->outerContext = CTXT;
657 ddata->lineNumber = instr.line;
658 ddata->columnNumber = instr.column;
660 QQmlComponentPrivate::get(qcomp)->creationContext = CTXT;
663 INSTRUCTIONSTREAM += instr.count;
664 QML_END_INSTR(CreateComponent)
666 QML_BEGIN_INSTR(StoreMetaObject)
667 QObject *target = objects.top();
670 const QByteArray &metadata = DATAS.at(instr.data);
671 QFastMetaBuilder::fromData(&mo, 0, metadata);
673 const QQmlVMEMetaData *data =
674 (const QQmlVMEMetaData *)DATAS.at(instr.aliasData).constData();
676 (void)new QQmlVMEMetaObject(target, &mo, data, COMP);
678 if (instr.propertyCache != -1) {
679 QQmlData *ddata = QQmlData::get(target, true);
680 if (ddata->propertyCache) ddata->propertyCache->release();
681 ddata->propertyCache = PROPERTYCACHES.at(instr.propertyCache);
682 ddata->propertyCache->addref();
684 QML_END_INSTR(StoreMetaObject)
686 QML_BEGIN_INSTR(AssignCustomType)
687 QObject *target = objects.top();
688 CLEAN_PROPERTY(target, instr.propertyIndex);
690 const QString &primitive = PRIMITIVES.at(instr.primitive);
691 int type = instr.type;
692 QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type);
693 QVariant v = (*converter)(primitive);
696 target->metaObject()->property(instr.propertyIndex);
697 if (v.isNull() || ((int)prop.type() != type && prop.userType() != type))
698 VME_EXCEPTION(tr("Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name())), instr.line);
700 void *a[] = { (void *)v.data(), 0, &status, &flags };
701 QMetaObject::metacall(target, QMetaObject::WriteProperty,
702 instr.propertyIndex, a);
703 QML_END_INSTR(AssignCustomType)
705 QML_BEGIN_INSTR(AssignSignalObject)
708 QObject *assign = objects.pop();
709 QObject *target = objects.top();
710 int sigIdx = instr.signal;
711 const QString &pr = PRIMITIVES.at(sigIdx);
713 QQmlProperty prop(target, pr);
714 if (prop.type() & QQmlProperty::SignalProperty) {
716 QMetaMethod method = QQmlMetaType::defaultMethod(assign);
717 if (!method.isValid())
718 VME_EXCEPTION(tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className())), instr.line);
720 if (!QMetaObject::checkConnectArgs(prop.method(), method)) {
721 VME_EXCEPTION(tr("Cannot connect mismatched signal/slot %1 %vs. %2")
722 .arg(QString::fromLatin1(method.methodSignature().constData()))
723 .arg(QString::fromLatin1(prop.method().methodSignature().constData())), instr.line);
726 QQmlPropertyPrivate::connect(target, prop.index(), assign, method.methodIndex());
729 VME_EXCEPTION(tr("Cannot assign an object to signal property %1").arg(pr), instr.line);
733 QML_END_INSTR(AssignSignalObject)
735 QML_BEGIN_INSTR(StoreSignal)
736 QObject *target = objects.top();
737 QObject *context = objects.at(objects.count() - 1 - instr.context);
739 QMetaMethod signal = target->metaObject()->method(instr.signalIndex);
741 QQmlBoundSignal *bs = new QQmlBoundSignal(target, signal, target);
742 QQmlBoundSignalExpression *expr =
743 new QQmlBoundSignalExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column);
744 bs->takeExpression(expr);
745 QML_END_INSTR(StoreSignal)
747 QML_BEGIN_INSTR(StoreImportedScript)
748 CTXT->importedScripts << run(CTXT, SCRIPTS.at(instr.value));
749 QML_END_INSTR(StoreImportedScript)
751 QML_BEGIN_INSTR(StoreScriptString)
752 QObject *target = objects.top();
753 QObject *scope = objects.at(objects.count() - 1 - instr.scope);
755 ss.setContext(CTXT->asQQmlContext());
756 ss.setScopeObject(scope);
757 ss.setScript(PRIMITIVES.at(instr.value));
758 ss.d.data()->bindingId = instr.bindingId;
759 ss.d.data()->lineNumber = instr.line;
760 ss.d.data()->columnNumber = instr.column;
762 void *a[] = { &ss, 0, &status, &flags };
763 QMetaObject::metacall(target, QMetaObject::WriteProperty,
764 instr.propertyIndex, a);
765 QML_END_INSTR(StoreScriptString)
767 QML_BEGIN_INSTR(BeginObject)
768 QObject *target = objects.top();
769 QQmlParserStatus *status = reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(target) + instr.castValue);
770 parserStatus.push(status);
771 #ifdef QML_ENABLE_TRACE
772 Q_ASSERT(QObjectPrivate::get(target)->declarativeData);
773 parserStatusData.push(static_cast<QQmlData *>(QObjectPrivate::get(target)->declarativeData));
775 status->d = &parserStatus.top();
777 status->classBegin();
778 QML_END_INSTR(BeginObject)
780 QML_BEGIN_INSTR(InitV8Bindings)
781 CTXT->v8bindings = new QV8Bindings(&PROGRAMS[instr.programIndex], instr.line, CTXT);
782 QML_END_INSTR(InitV8Bindings)
784 QML_BEGIN_INSTR(StoreBinding)
786 objects.at(objects.count() - 1 - instr.owner);
788 objects.at(objects.count() - 1 - instr.context);
790 if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
791 QML_NEXT_INSTR(StoreBinding);
793 QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true,
794 context, CTXT, COMP->name, instr.line,
796 bindValues.push(bind);
797 bind->m_mePtr = &bindValues.top();
798 bind->setTarget(target, instr.property, CTXT);
801 QQmlAbstractBinding *old =
802 QQmlPropertyPrivate::setBindingNoEnable(target,
803 instr.property.coreIndex,
804 instr.property.getValueTypeCoreIndex(),
806 if (old) { old->destroy(); }
808 typedef QQmlPropertyPrivate QDPP;
809 Q_ASSERT(bind->propertyIndex() == QDPP::bindingIndex(instr.property));
810 Q_ASSERT(bind->object() == target);
812 CLEAN_PROPERTY(target, QDPP::bindingIndex(instr.property));
816 QML_END_INSTR(StoreBinding)
818 QML_BEGIN_INSTR(StoreV4Binding)
820 objects.at(objects.count() - 1 - instr.owner);
822 objects.at(objects.count() - 1 - instr.context);
824 int property = instr.property;
825 if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF))
826 QML_NEXT_INSTR(StoreV4Binding);
828 QQmlAbstractBinding *binding =
829 CTXT->v4bindings->configBinding(instr.value, target, scope, property,
830 instr.line, instr.column);
831 bindValues.push(binding);
832 binding->m_mePtr = &bindValues.top();
834 Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF));
835 Q_ASSERT(binding->object() == target);
837 CLEAN_PROPERTY(target, property & 0xFF00FFFF);
839 binding->addToObject();
840 QML_END_INSTR(StoreV4Binding)
842 QML_BEGIN_INSTR(StoreV8Binding)
844 objects.at(objects.count() - 1 - instr.owner);
846 objects.at(objects.count() - 1 - instr.context);
848 if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex))
849 QML_NEXT_INSTR(StoreV8Binding);
851 QQmlAbstractBinding *binding = CTXT->v8bindings->configBinding(target, scope,
854 bindValues.push(binding);
855 binding->m_mePtr = &bindValues.top();
858 QQmlAbstractBinding *old =
859 QQmlPropertyPrivate::setBindingNoEnable(target,
860 instr.property.coreIndex,
861 instr.property.getValueTypeCoreIndex(),
863 if (old) { old->destroy(); }
865 typedef QQmlPropertyPrivate QDPP;
866 Q_ASSERT(binding->propertyIndex() == QDPP::bindingIndex(instr.property));
867 Q_ASSERT(binding->object() == target);
869 CLEAN_PROPERTY(target, QDPP::bindingIndex(instr.property));
871 binding->addToObject();
874 QML_END_INSTR(StoreV8Binding)
876 QML_BEGIN_INSTR(StoreValueSource)
877 QObject *obj = objects.pop();
878 QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(obj) + instr.castValue);
879 QObject *target = objects.at(objects.count() - 1 - instr.owner);
881 obj->setParent(target);
882 vs->setTarget(QQmlPropertyPrivate::restore(target, instr.property, CTXT));
883 QML_END_INSTR(StoreValueSource)
885 QML_BEGIN_INSTR(StoreValueInterceptor)
886 QObject *obj = objects.pop();
887 QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(obj) + instr.castValue);
888 QObject *target = objects.at(objects.count() - 1 - instr.owner);
890 QQmlPropertyPrivate::restore(target, instr.property, CTXT);
891 obj->setParent(target);
893 QQmlVMEMetaObject *mo = static_cast<QQmlVMEMetaObject *>((QMetaObject*)target->metaObject());
894 mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi);
895 QML_END_INSTR(StoreValueInterceptor)
897 QML_BEGIN_INSTR(StoreObjectQList)
898 QObject *assign = objects.pop();
900 const List &list = lists.top();
901 list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, assign);
902 QML_END_INSTR(StoreObjectQList)
904 QML_BEGIN_INSTR(AssignObjectList)
905 // This is only used for assigning interfaces
906 QObject *assign = objects.pop();
907 const List &list = lists.top();
909 int type = list.type;
913 const char *iid = QQmlMetaType::interfaceIId(type);
915 ptr = assign->qt_metacast(iid);
917 VME_EXCEPTION(tr("Cannot assign object to list"), instr.line);
920 list.qListProperty.append((QQmlListProperty<void>*)&list.qListProperty, ptr);
921 QML_END_INSTR(AssignObjectList)
923 QML_BEGIN_INSTR(StoreInterface)
924 QObject *assign = objects.pop();
925 QObject *target = objects.top();
926 CLEAN_PROPERTY(target, instr.propertyIndex);
928 int coreIdx = instr.propertyIndex;
929 QMetaProperty prop = target->metaObject()->property(coreIdx);
930 int t = prop.userType();
931 const char *iid = QQmlMetaType::interfaceIId(t);
934 void *ptr = assign->qt_metacast(iid);
936 void *a[] = { &ptr, 0, &status, &flags };
937 QMetaObject::metacall(target,
938 QMetaObject::WriteProperty,
945 VME_EXCEPTION(tr("Cannot assign object to interface property"), instr.line);
946 QML_END_INSTR(StoreInterface)
948 QML_BEGIN_INSTR(FetchAttached)
949 QObject *target = objects.top();
951 QObject *qmlObject = qmlAttachedPropertiesObjectById(instr.id, target);
954 VME_EXCEPTION(tr("Unable to create attached object"), instr.line);
956 objects.push(qmlObject);
957 QML_END_INSTR(FetchAttached)
959 QML_BEGIN_INSTR(FetchQList)
960 QObject *target = objects.top();
962 lists.push(List(instr.type));
965 a[0] = (void *)&(lists.top().qListProperty);
966 QMetaObject::metacall(target, QMetaObject::ReadProperty,
968 QML_END_INSTR(FetchQList)
970 QML_BEGIN_INSTR(FetchObject)
971 QObject *target = objects.top();
974 // NOTE: This assumes a cast to QObject does not alter the
978 QMetaObject::metacall(target, QMetaObject::ReadProperty,
982 VME_EXCEPTION(tr("Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line);
985 QML_END_INSTR(FetchObject)
987 QML_BEGIN_INSTR(PopQList)
989 QML_END_INSTR(PopQList)
991 QML_BEGIN_INSTR(Defer)
992 if (instr.deferCount) {
993 QObject *target = objects.top();
995 QQmlData::get(target, true);
997 data->deferredComponent = COMP;
998 data->deferredIdx = INSTRUCTIONSTREAM - COMP->bytecode.constData();
999 INSTRUCTIONSTREAM += instr.deferCount;
1001 QML_END_INSTR(Defer)
1003 QML_BEGIN_INSTR(PopFetchedObject)
1005 QML_END_INSTR(PopFetchedObject)
1007 QML_BEGIN_INSTR(FetchValueType)
1008 QObject *target = objects.top();
1010 if (instr.bindingSkipList != 0) {
1011 // Possibly need to clear bindings
1012 QQmlData *targetData = QQmlData::get(target);
1014 QQmlAbstractBinding *binding =
1015 QQmlPropertyPrivate::binding(target, instr.property, -1);
1017 if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) {
1018 QQmlPropertyPrivate::setBinding(target, instr.property, -1, 0);
1020 } else if (binding) {
1021 QQmlValueTypeProxyBinding *proxy =
1022 static_cast<QQmlValueTypeProxyBinding *>(binding);
1023 proxy->removeBindings(instr.bindingSkipList);
1028 QQmlValueType *valueHandler = ep->valueTypes[instr.type];
1029 valueHandler->read(target, instr.property);
1030 objects.push(valueHandler);
1031 QML_END_INSTR(FetchValueType)
1033 QML_BEGIN_INSTR(PopValueType)
1034 QQmlValueType *valueHandler =
1035 static_cast<QQmlValueType *>(objects.pop());
1036 QObject *target = objects.top();
1037 valueHandler->write(target, instr.property, QQmlPropertyPrivate::BypassInterceptor);
1038 QML_END_INSTR(PopValueType)
1040 #ifdef QML_THREADED_VME_INTERPRETER
1044 qFatal("QQmlCompiledData: Internal error - unknown instruction %d", genericInstr->common.instructionType);
1051 Q_ASSERT(!states.isEmpty());
1052 Q_ASSERT(!errors->isEmpty());
1059 Q_ASSERT(objects.count() == 1);
1061 QObject *rv = objects.top();
1063 objects.deallocate();
1070 void QQmlVME::reset()
1072 Q_ASSERT(!states.isEmpty() || objects.isEmpty());
1074 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
1076 if (!objects.isEmpty() && !(states.at(0).flags & State::Deferred))
1077 delete objects.at(0);
1079 if (!rootContext.isNull())
1080 rootContext->activeVMEData = 0;
1082 // Remove the QQmlParserStatus and QQmlAbstractBinding back pointers
1083 blank(parserStatus);
1086 while (componentAttached) {
1087 QQmlComponentAttached *a = componentAttached;
1092 objects.deallocate();
1094 bindValues.deallocate();
1095 parserStatus.deallocate();
1096 #ifdef QML_ENABLE_TRACE
1097 parserStatusData.deallocate();
1099 finalizeCallbacks.clear();
1102 creationContext = 0;
1105 // Must be called with a handle scope and context
1106 void QQmlScriptData::initialize(QQmlEngine *engine)
1108 Q_ASSERT(m_program.IsEmpty());
1110 Q_ASSERT(!hasEngine());
1112 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1113 QV8Engine *v8engine = ep->v8engine();
1115 // If compilation throws an error, a surrounding v8::TryCatch will record it.
1116 v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_programSource.constData(),
1117 m_programSource.length(), urlString, 1);
1118 if (program.IsEmpty())
1121 m_program = qPersistentNew<v8::Script>(program);
1122 m_programSource.clear(); // We don't need this anymore
1124 addToEngine(engine);
1129 v8::Persistent<v8::Object> QQmlVME::run(QQmlContextData *parentCtxt, QQmlScriptData *script)
1131 if (script->m_loaded)
1132 return qPersistentNew<v8::Object>(script->m_value);
1134 Q_ASSERT(parentCtxt && parentCtxt->engine);
1135 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
1136 QV8Engine *v8engine = ep->v8engine();
1138 bool shared = script->pragmas & QQmlScript::Object::ScriptBlock::Shared;
1140 QQmlContextData *effectiveCtxt = parentCtxt;
1144 // Create the script context if required
1145 QQmlContextData *ctxt = new QQmlContextData;
1146 ctxt->isInternal = true;
1147 ctxt->isJSContext = true;
1149 ctxt->isPragmaLibraryContext = true;
1151 ctxt->isPragmaLibraryContext = parentCtxt->isPragmaLibraryContext;
1152 ctxt->url = script->url;
1153 ctxt->urlString = script->urlString;
1155 // For backward compatibility, if there are no imports, we need to use the
1156 // imports from the parent context. See QTBUG-17518.
1157 if (!script->importCache->isEmpty()) {
1158 ctxt->imports = script->importCache;
1159 } else if (effectiveCtxt) {
1160 ctxt->imports = effectiveCtxt->imports;
1161 ctxt->importedScripts = effectiveCtxt->importedScripts;
1162 for (int ii = 0; ii < ctxt->importedScripts.count(); ++ii)
1163 ctxt->importedScripts[ii] = qPersistentNew<v8::Object>(ctxt->importedScripts[ii]);
1166 if (ctxt->imports) {
1167 ctxt->imports->addref();
1170 if (effectiveCtxt) {
1171 ctxt->setParent(effectiveCtxt, true);
1173 ctxt->engine = parentCtxt->engine; // Fix for QTBUG-21620
1176 for (int ii = 0; ii < script->scripts.count(); ++ii) {
1177 ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData());
1180 v8::HandleScope handle_scope;
1181 v8::Context::Scope scope(v8engine->context());
1183 v8::TryCatch try_catch;
1184 if (!script->isInitialized())
1185 script->initialize(parentCtxt->engine);
1187 v8::Local<v8::Object> qmlglobal = v8engine->qmlScope(ctxt, 0);
1189 if (!script->m_program.IsEmpty()) {
1190 script->m_program->Run(qmlglobal);
1192 // Compilation failed.
1193 Q_ASSERT(try_catch.HasCaught());
1196 v8::Persistent<v8::Object> rv;
1198 if (try_catch.HasCaught()) {
1199 v8::Local<v8::Message> message = try_catch.Message();
1200 if (!message.IsEmpty()) {
1202 QQmlExpressionPrivate::exceptionToError(message, error);
1207 rv = qPersistentNew<v8::Object>(qmlglobal);
1209 script->m_value = qPersistentNew<v8::Object>(qmlglobal);
1210 script->m_loaded = true;
1216 #ifdef QML_THREADED_VME_INTERPRETER
1217 void **QQmlVME::instructionJumpTable()
1219 static void **jumpTable = 0;
1222 QQmlVME::Interrupt i;
1223 dummy.run(0, i, &jumpTable);
1229 QQmlContextData *QQmlVME::complete(const Interrupt &interrupt)
1232 (bindValues.isEmpty() &&
1233 parserStatus.isEmpty() &&
1234 componentAttached == 0 &&
1235 rootContext.isNull() &&
1236 finalizeCallbacks.isEmpty()));
1241 QQmlTrace trace("VME Complete");
1242 #ifdef QML_ENABLE_TRACE
1243 trace.addDetail("URL", rootComponent->url);
1246 ActiveVMERestorer restore(this, QQmlEnginePrivate::get(engine));
1247 QRecursionWatcher<QQmlVME, &QQmlVME::recursion> watcher(this);
1250 QQmlTrace trace("VME Binding Enable");
1251 trace.event("begin binding eval");
1252 while (!bindValues.isEmpty()) {
1253 QQmlAbstractBinding *b = bindValues.pop();
1257 b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
1258 QQmlPropertyPrivate::DontRemoveBinding);
1261 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1264 bindValues.deallocate();
1268 QQmlTrace trace("VME Component Complete");
1269 while (!parserStatus.isEmpty()) {
1270 QQmlParserStatus *status = parserStatus.pop();
1271 #ifdef QML_ENABLE_TRACE
1272 QQmlData *data = parserStatusData.pop();
1275 if (status && status->d) {
1277 #ifdef QML_ENABLE_TRACE
1278 QQmlTrace trace("Component complete");
1279 trace.addDetail("URL", data->outerContext->url);
1280 trace.addDetail("Line", data->lineNumber);
1282 status->componentComplete();
1285 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1288 parserStatus.deallocate();
1292 QQmlTrace trace("VME Finalize Callbacks");
1293 for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) {
1294 QQmlEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii);
1295 QObject *obj = callback.first;
1297 void *args[] = { 0 };
1298 QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args);
1300 if (watcher.hasRecursed())
1303 finalizeCallbacks.clear();
1307 QQmlTrace trace("VME Component.onCompleted Callbacks");
1308 while (componentAttached) {
1309 QQmlComponentAttached *a = componentAttached;
1311 QQmlData *d = QQmlData::get(a->parent());
1313 Q_ASSERT(d->context);
1314 a->add(&d->context->componentAttached);
1315 emit a->completed();
1317 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1322 QQmlContextData *rv = rootContext;
1326 if (rv) rv->activeVMEData = data;
1331 void QQmlVME::blank(QFiniteStack<QQmlAbstractBinding *> &bs)
1333 for (int ii = 0; ii < bs.count(); ++ii) {
1334 QQmlAbstractBinding *b = bs.at(ii);
1335 if (b) b->m_mePtr = 0;
1339 void QQmlVME::blank(QFiniteStack<QQmlParserStatus *> &pss)
1341 for (int ii = 0; ii < pss.count(); ++ii) {
1342 QQmlParserStatus *ps = pss.at(ii);
1347 QQmlVMEGuard::QQmlVMEGuard()
1348 : m_objectCount(0), m_objects(0), m_contextCount(0), m_contexts(0)
1352 QQmlVMEGuard::~QQmlVMEGuard()
1357 void QQmlVMEGuard::guard(QQmlVME *vme)
1361 m_objectCount = vme->objects.count();
1362 m_objects = new QQmlGuard<QObject>[m_objectCount];
1363 for (int ii = 0; ii < m_objectCount; ++ii)
1364 m_objects[ii] = vme->objects[ii];
1366 m_contextCount = (vme->rootContext.isNull()?0:1) + vme->states.count();
1367 m_contexts = new QQmlGuardedContextData[m_contextCount];
1368 for (int ii = 0; ii < vme->states.count(); ++ii)
1369 m_contexts[ii] = vme->states.at(ii).context;
1370 if (!vme->rootContext.isNull())
1371 m_contexts[m_contextCount - 1] = vme->rootContext.contextData();
1374 void QQmlVMEGuard::clear()
1376 delete [] m_objects;
1377 delete [] m_contexts;
1385 bool QQmlVMEGuard::isOK() const
1387 for (int ii = 0; ii < m_objectCount; ++ii)
1388 if (m_objects[ii].isNull())
1391 for (int ii = 0; ii < m_contextCount; ++ii)
1392 if (m_contexts[ii].isNull())