Instruction::StoreV4Binding store;
store.value = js.compiledIndex;
+ store.fallbackValue = js.sharedIndex;
store.context = js.bindingContext.stack;
store.owner = js.bindingContext.owner;
store.isAlias = prop->isAlias;
store.line = binding->location.start.line;
store.column = binding->location.start.column;
output->addInstruction(store);
+
+ if (store.fallbackValue > -1) {
+ //also create v8 instruction (needed to properly configure the fallback v8 binding)
+ JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
+ js.dataType = BindingReference::V8;
+ genBindingAssignment(binding, prop, obj, valueTypeProperty);
+ }
} else if (ref.dataType == BindingReference::V8) {
const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
Instruction::StoreV8Binding store;
- store.value = js.compiledIndex;
+ store.value = js.sharedIndex;
store.context = js.bindingContext.stack;
store.owner = js.bindingContext.owner;
store.isAlias = prop->isAlias;
} else {
store.isRoot = (compileState->root == obj);
}
+ store.isFallback = js.compiledIndex > -1;
store.line = binding->location.start.line;
store.column = binding->location.start.column;
} else {
store.isRoot = (compileState->root == obj);
}
+ store.isFallback = false;
Q_ASSERT(js.bindingContext.owner == 0 ||
(js.bindingContext.owner != 0 && valueTypeProperty));
expr.property = binding.property;
expr.expression = binding.expression;
- int index = bindingCompiler.compile(expr, enginePrivate);
+ bool needsFallback = false;
+ int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
if (index != -1) {
+ // Ensure the index value fits within the available space
+ Q_ASSERT(index < (1 << 15));
+
binding.dataType = BindingReference::V4;
binding.compiledIndex = index;
+ binding.sharedIndex = -1;
if (componentStats)
componentStats->componentStat.optimizedBindings.append(b->value->location);
- continue;
+
+ if (!needsFallback)
+ continue;
+
+ // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
}
// Pre-rewrite the expression
binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable);
if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
- binding.dataType = BindingReference::V8;
sharedBindings.append(b);
- if (componentStats)
- componentStats->componentStat.sharedBindings.append(b->value->location);
+ if (!needsFallback) {
+ binding.dataType = BindingReference::V8;
+ binding.compiledIndex = -1;
+
+ if (componentStats)
+ componentStats->componentStat.sharedBindings.append(b->value->location);
+ }
} else {
+ Q_ASSERT(!needsFallback);
binding.dataType = BindingReference::QtScript;
if (componentStats)
functionArray += expression.toUtf8();
lineNumber += expression.count(QLatin1Char('\n'));
- reference->compiledIndex = ii;
+
+ // Ensure the index value fits within the available space
+ Q_ASSERT(ii < (1 << 15));
+ reference->sharedIndex = ii;
}
functionArray.append("]", 1);
QQmlScript::Property *property;
QQmlScript::Value *value;
- int compiledIndex;
+ int compiledIndex:15;
+ int sharedIndex:15;
QString rewrittenExpression;
BindingContext bindingContext;
QML_INSTR_HEADER
unsigned int property;
int value;
+ int fallbackValue;
short context;
short owner;
bool isRoot;
short owner;
bool isRoot;
bool isAlias;
+ bool isFallback;
ushort line;
ushort column;
};
#include "qqmlvmemetaobject_p.h"
#include "qqmlexpression_p.h"
#include "qqmlvaluetypeproxybinding_p.h"
+#include <private/qv8bindings_p.h>
#include <QStringList>
#include <private/qmetaobject_p.h>
}
/*!
+ Activates a shared binding which was previously created but not added to the
+ object. This is needed when an optimized binding is invalidated.
+*/
+QQmlAbstractBinding *QQmlPropertyPrivate::activateSharedBinding(QQmlContextData *context,
+ int sharedIdx, WriteFlags flags)
+{
+ QQmlAbstractBinding *newBinding = 0;
+ newBinding = context->v8bindings->binding(sharedIdx);
+
+ if (!newBinding)
+ return newBinding;
+
+ // This binding now references the bindings object
+ context->v8bindings->addref();
+
+ QObject *object = newBinding->object();
+ int pi = newBinding->propertyIndex();
+
+ int core = pi & 0xFFFFFF;
+ int vt = (pi & 0xFF000000)?(pi >> 24):-1;
+
+ return setBinding(object, core, vt, newBinding, flags);
+}
+
+/*!
Returns the expression associated with this signal property, or 0 if no
signal expression exists.
*/
static QQmlAbstractBinding *setBindingNoEnable(QObject *, int coreIndex,
int valueTypeIndex /* -1 */,
QQmlAbstractBinding *);
+ static QQmlAbstractBinding *activateSharedBinding(QQmlContextData *context,
+ int sharedIdx, WriteFlags flags);
static QQmlAbstractBinding *binding(QObject *, int coreIndex,
int valueTypeIndex /* -1 */);
if (QQmlPropertyData **old = stringCache.value(string)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
propertyIndexCache.append(data);
if (QQmlPropertyData **old = stringCache.value(name)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
propertyIndexCache.append(data);
if (QQmlPropertyData **old = stringCache.value(string)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
methodIndexCache.append(data);
if (QQmlPropertyData **old = stringCache.value(name)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
methodIndexCache.append(data);
if (QQmlPropertyData **old = stringCache.value(string)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
methodIndexCache.append(data);
if (QQmlPropertyData **old = stringCache.value(name)) {
data.overrideIndexIsProperty = !(*old)->isFunction();
data.overrideIndex = (*old)->coreIndex;
+ (*old)->flags |= QQmlPropertyData::IsOverridden;
}
methodIndexCache.append(data);
data->flags |= QQmlPropertyData::IsOverload;
data->overrideIndexIsProperty = !old->isFunction();
data->overrideIndex = old->coreIndex;
+ old->flags |= QQmlPropertyData::IsOverridden;
}
}
} else if (old) {
data->overrideIndexIsProperty = !old->isFunction();
data->overrideIndex = old->coreIndex;
+ old->flags |= QQmlPropertyData::IsOverridden;
}
}
}
IsResettable = 0x00000004, // Has RESET function
IsAlias = 0x00000008, // Is a QML alias to another property
IsFinal = 0x00000010, // Has FINAL flag
- IsDirect = 0x00000020, // Exists on a C++ QMetaObject
- HasAccessors = 0x00000040, // Has property accessors
+ IsOverridden = 0x00000020, // Is overridden by a extension property
+ IsDirect = 0x00000040, // Exists on a C++ QMetaObject
+ HasAccessors = 0x00000080, // Has property accessors
// These are mutualy exclusive
- IsFunction = 0x00000080, // Is an invokable
- IsQObjectDerived = 0x00000100, // Property type is a QObject* derived type
- IsEnumType = 0x00000200, // Property type is an enum
- IsQList = 0x00000400, // Property type is a QML list
- IsQmlBinding = 0x00000800, // Property type is a QQmlBinding*
- IsQJSValue = 0x00001000, // Property type is a QScriptValue
- IsV8Handle = 0x00002000, // Property type is a QQmlV8Handle
- IsVarProperty = 0x00004000, // Property type is a "var" property of VMEMO
- IsValueTypeVirtual = 0x00008000, // Property is a value type "virtual" property
- IsQVariant = 0x00010000, // Property is a QVariant
+ IsFunction = 0x00000100, // Is an invokable
+ IsQObjectDerived = 0x00000200, // Property type is a QObject* derived type
+ IsEnumType = 0x00000400, // Property type is an enum
+ IsQList = 0x00000800, // Property type is a QML list
+ IsQmlBinding = 0x00001000, // Property type is a QQmlBinding*
+ IsQJSValue = 0x00002000, // Property type is a QScriptValue
+ IsV8Handle = 0x00004000, // Property type is a QQmlV8Handle
+ IsVarProperty = 0x00008000, // Property type is a "var" property of VMEMO
+ IsValueTypeVirtual = 0x00010000, // Property is a value type "virtual" property
+ IsQVariant = 0x00020000, // Property is a QVariant
// Apply only to IsFunctions
- IsVMEFunction = 0x00020000, // Function was added by QML
- HasArguments = 0x00040000, // Function takes arguments
- IsSignal = 0x00080000, // Function is a signal
- IsVMESignal = 0x00100000, // Signal was added by QML
- IsV8Function = 0x00200000, // Function takes QQmlV8Function* args
- IsSignalHandler = 0x00400000, // Function is a signal handler
- IsOverload = 0x00800000, // Function is an overload of another function
- IsCloned = 0x01000000, // The function was marked as cloned
+ IsVMEFunction = 0x00040000, // Function was added by QML
+ HasArguments = 0x00080000, // Function takes arguments
+ IsSignal = 0x00100000, // Function is a signal
+ IsVMESignal = 0x00200000, // Signal was added by QML
+ IsV8Function = 0x00400000, // Function takes QQmlV8Function* args
+ IsSignalHandler = 0x00800000, // Function is a signal handler
+ IsOverload = 0x01000000, // Function is an overload of another function
+ IsCloned = 0x02000000, // The function was marked as cloned
// Internal QQmlPropertyCache flags
- NotFullyResolved = 0x02000000, // True if the type data is to be lazily resolved
+ NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved
// Flags that are set based on the propType field
PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue |
bool isResettable() const { return flags & IsResettable; }
bool isAlias() const { return flags & IsAlias; }
bool isFinal() const { return flags & IsFinal; }
+ bool isOverridden() const { return flags & IsOverridden; }
bool isDirect() const { return flags & IsDirect; }
bool hasAccessors() const { return flags & HasAccessors; }
bool isFunction() const { return flags & IsFunction; }
QML_NEXT_INSTR(StoreV4Binding);
QQmlAbstractBinding *binding =
- CTXT->v4bindings->configBinding(instr.value, target, scope, property,
+ CTXT->v4bindings->configBinding(instr.value, instr.fallbackValue, target, scope, property,
instr.line, instr.column);
bindValues.push(binding);
binding->m_mePtr = &bindValues.top();
QQmlAbstractBinding *binding = CTXT->v8bindings->configBinding(target, scope,
&instr);
- if (binding) {
+ if (binding && !instr.isFallback) {
bindValues.push(binding);
binding->m_mePtr = &bindValues.top();
delete [] subscriptions; subscriptions = 0;
}
-QQmlAbstractBinding *QV4Bindings::configBinding(int index, QObject *target,
+QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target,
QObject *scope, int property,
int line, int column)
{
Binding *rv = bindings + index;
rv->index = index;
+ rv->fallbackIndex = fallbackIndex;
rv->property = property;
rv->target = target;
rv->scope = scope;
return;
}
+ bool invalidated = false;
+ bool *inv = (binding->fallbackIndex != -1) ? &invalidated : 0;
+
binding->updating = true;
if (binding->property & 0xFFFF0000) {
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
vt->read(*binding->target, binding->property & 0xFFFF);
QObject *target = vt;
- run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags);
+ run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv);
- vt->write(*binding->target, binding->property & 0xFFFF, flags);
+ if (!invalidated) {
+ vt->write(*binding->target, binding->property & 0xFFFF, flags);
+ }
} else {
QQmlData *data = QQmlData::get(*binding->target);
QQmlPropertyData *propertyData = (data && data->propertyCache ? data->propertyCache->property(binding->property) : 0);
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(QQmlEnginePrivate::get(context->engine)->v8engine()->context());
- run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags);
+ run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv);
} else {
- run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags);
+ run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv);
}
}
binding->updating = false;
+
+ if (invalidated) {
+ // This binding is no longer valid - fallback to V8
+ Q_ASSERT(binding->fallbackIndex > -1);
+ QQmlAbstractBinding *b = QQmlPropertyPrivate::activateSharedBinding(context, binding->fallbackIndex, flags);
+ Q_ASSERT(b == binding);
+ b->destroy();
+ }
}
MARK_REGISTER(reg); \
}
+//TODO: avoid construction of name and name-based lookup
+#define INVALIDATION_CHECK(inv, obj, index) { \
+ if ((inv) != 0) { \
+ QQmlData *data = QQmlData::get((obj)); \
+ if (data && !data->propertyCache) { \
+ data->propertyCache = QQmlEnginePrivate::get(context->engine)->cache(object); \
+ if (data->propertyCache) data->propertyCache->addref(); \
+ } \
+ QQmlPropertyData *prop = (data && data->propertyCache) ? data->propertyCache->property((index)) : 0; \
+ if (prop && prop->isOverridden()) { \
+ int resolvedIndex = data->propertyCache->property(prop->name(obj))->coreIndex; \
+ if (index < resolvedIndex) { \
+ *(inv) = true; \
+ goto programExit; \
+ } \
+ } \
+ } \
+}
+
#ifdef QML_THREADED_INTERPRETER
void **QV4Bindings::getDecodeInstrTable()
{
quint32 executedBlocks = 0;
dummy->run(0, executedBlocks, 0, 0, 0, 0,
QQmlPropertyPrivate::BypassInterceptor,
- &decode_instr);
+ 0, &decode_instr);
dummy->release();
}
return decode_instr;
void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
QQmlContextData *context, QQmlDelayedError *error,
QObject *scope, QObject *output,
- QQmlPropertyPrivate::WriteFlags storeFlags
+ QQmlPropertyPrivate::WriteFlags storeFlags,
+ bool *invalidated
#ifdef QML_THREADED_INTERPRETER
,void ***table
#endif
QObject *object = reg.getQObject();
if (!object) {
- reg.setUndefined();
+ THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId);
} else {
+ INVALIDATION_CHECK(invalidated, object, instr->fetchAndSubscribe.property.coreIndex);
+
int subIdx = instr->fetchAndSubscribe.subscription;
Subscription *sub = 0;
if (subIdx != -1) {
if (!object) {
THROW_EXCEPTION(instr->fetch.exceptionId);
} else {
+ INVALIDATION_CHECK(invalidated, object, instr->fetch.index);
+
const Register::Type valueType = (Register::Type)instr->fetch.valueType;
reg.init(valueType);
if (instr->fetch.valueType >= FirstCleanupType)
QV4Bindings(const char *program, QQmlContextData *context);
virtual ~QV4Bindings();
- QQmlAbstractBinding *configBinding(int index, QObject *target,
+ QQmlAbstractBinding *configBinding(int index, int fallbackIndex, QObject *target,
QObject *scope, int property,
int line, int column);
#endif
struct Binding : public QQmlAbstractBinding, public QQmlDelayedError {
- Binding() : QQmlAbstractBinding(V4), enabled(false), updating(0), property(0),
- scope(0), target(0), executedBlocks(0), parent(0) {}
+ Binding() : QQmlAbstractBinding(V4), index(-1), fallbackIndex(-1), enabled(false),
+ updating(0), property(0), scope(0), target(0), executedBlocks(0), parent(0) {}
// Inherited from QQmlAbstractBinding
static void destroy(QQmlAbstractBinding *);
int targetProperty;
};
- int index:30;
+ int index:15;
+ int fallbackIndex:15;
bool enabled:1;
bool updating:1;
+
// Encoding of property is coreIndex | (propType << 16) | (valueTypeIndex << 24)
// propType and valueTypeIndex are only set if the property is a value type property
int property;
void init();
void run(int instr, quint32 &executedBlocks, QQmlContextData *context,
QQmlDelayedError *error, QObject *scope, QObject *output,
- QQmlPropertyPrivate::WriteFlags storeFlags
+ QQmlPropertyPrivate::WriteFlags storeFlags,
+ bool *invalidated
#ifdef QML_THREADED_INTERPRETER
, void ***decode_instr = 0
#endif
QV4CompilerPrivate::QV4CompilerPrivate()
: subscriptionOffset(0)
, _function(0) , _block(0) , _discarded(false), registerCount(0)
- , bindingLine(0), bindingColumn(0)
+ , bindingLine(0), bindingColumn(0), invalidatable(false)
{
}
patches.clear();
pool.clear();
currentReg = 0;
+ invalidatable = false;
}
/*!
IR::Function thisFunction(&pool), *function = &thisFunction;
QV4IRBuilder irBuilder(expression, engine);
- if (!irBuilder(function, node))
+ if (!irBuilder(function, node, &invalidatable))
return false;
bool discarded = false;
/*
-1 on failure, otherwise the binding index to use.
*/
-int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine)
+int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine, bool *invalidatable)
{
if (!expression.expression.asAST()) return false;
d->engine = engine;
if (d->compile(expression.expression.asAST())) {
+ *invalidatable = d->isInvalidatable();
return d->commitCompile();
} else {
return -1;
};
// -1 on failure, otherwise the binding index to use
- int compile(const Expression &, QQmlEnginePrivate *);
+ int compile(const Expression &, QQmlEnginePrivate *, bool *);
// Returns the compiled program
QByteArray program() const;
bool compile(QQmlJS::AST::Node *);
+ bool isInvalidatable() const { return invalidatable; }
+
int registerLiteralString(quint8 reg, const QStringRef &);
QByteArray data;
quint32 currentBlockMask;
int bindingLine;
int bindingColumn;
+ bool invalidatable;
};
}
}
-QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr,
- QQmlEnginePrivate *engine)
-: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false)
+QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr,
+ QQmlEnginePrivate *engine)
+: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false),
+ _invalidatable(false)
{
}
bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function,
- QQmlJS::AST::Node *ast)
+ QQmlJS::AST::Node *ast, bool *invalidatable)
{
bool discarded = false;
qSwap(_function, function);
qSwap(_discard, discarded);
+ *invalidatable = _invalidatable;
return !discarded;
}
if (!data || data->isFunction())
return false; // Don't support methods (or non-existing properties ;)
- if(!data->isFinal()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** non-final attached property:"
- << (*baseName->id + QLatin1Char('.') + ast->name.toString());
- return false; // We don't know enough about this property
- }
+ if (!data->isFinal())
+ _invalidatable = true;
IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
_expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data, line, column);
if (!data || data->isFunction())
return false; // Don't support methods (or non-existing properties ;)
- if (!data->isFinal()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** non-final attached property:"
- << (*baseName->id + QLatin1Char('.') + ast->name.toString());
- return false; // We don't know enough about this property
- }
+ if (!data->isFinal())
+ _invalidatable = true;
IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
_expr.code = _block->SYMBOL(baseName, irType, name, baseName->meta, data, line, column);
break;
case IR::Name::Property:
- if (baseName->type == IR::ObjectType && !baseName->meta.isNull() &&
- baseName->property->isFinal()) {
+ if (baseName->type == IR::ObjectType && !baseName->meta.isNull()) {
QQmlMetaObject meta = m_engine->metaObjectForType(baseName->property->propType);
QQmlPropertyCache *cache = meta.propertyCache(m_engine);
if (!cache)
return false;
if (QQmlPropertyData *data = cache->property(name)) {
- if (!data->isFinal()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** non-final property access:"
- << (*baseName->id + QLatin1Char('.') + ast->name.toString());
- return false; // We don't know enough about this property
- }
+ if (!baseName->property->isFinal() || !data->isFinal())
+ _invalidatable = true;
IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
_expr.code = _block->SYMBOL(baseName, irType, name,
public:
QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *);
- bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *);
+ bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *, bool *invalidatable);
protected:
struct ExprResult {
QQmlJS::IR::Function *_function;
QQmlJS::IR::BasicBlock *_block;
bool _discard;
+ bool _invalidatable;
ExprResult _expr;
};
rv->setNotifyOnValueChanged(true);
rv->parent = this;
- addref(); // This is decremented in Binding::destroy()
+ if (!i->isFallback)
+ addref(); // This is decremented in Binding::destroy()
return rv;
}
inline void addref();
inline void release();
+ QQmlAbstractBinding *binding(int index) const { return bindings + index; }
+
private:
Q_DISABLE_COPY(QV8Bindings)
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property Item baz: Item { width: 100 }
+ property string bar: baz.width
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ BaseComponent {
+ id: foo
+ }
+
+ Component.onCompleted: success = (foo.bar == '100')
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ BaseComponent {
+ id: foo
+ property Text baz: Text { width: 200 }
+ }
+
+ Component.onCompleted: success = (foo.bar == '200')
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test.fallbackBindingsObject 1.0 as ModuleAPI
+
+Item {
+ property bool success: false
+ property string foo: ModuleAPI.test
+
+ Component.onCompleted: success = (foo == '100')
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test.fallbackBindingsDerived 1.0 as ModuleAPI
+
+Item {
+ property bool success: false
+ property string foo: ModuleAPI.test
+
+ Component.onCompleted: success = (foo == 'hello')
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test.fallbackBindingsObject 1.0
+
+Item {
+ property bool success: false
+ property string foo: FallbackBindingsType.test
+
+ Component.onCompleted: success = (foo == '100')
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test.fallbackBindingsDerived 1.0
+
+Item {
+ property bool success: false
+ property string foo: FallbackBindingsType.test
+
+ Component.onCompleted: success = (foo == 'hello')
+}
return o;
}
+static QObject *fallback_bindings_object(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ return new FallbackBindingsObject();
+}
+
+static QObject *fallback_bindings_derived(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ return new FallbackBindingsDerived();
+}
+
class MyWorkerObjectThread : public QThread
{
public:
qmlRegisterType<MySequenceConversionObject>("Qt.test", 1, 0, "MySequenceConversionObject");
qmlRegisterType<MyUnregisteredEnumTypeObject>("Qt.test", 1, 0, "MyUnregisteredEnumTypeObject");
+
+ qmlRegisterModuleApi<FallbackBindingsObject>("Qt.test.fallbackBindingsObject", 1, 0, fallback_bindings_object);
+ qmlRegisterModuleApi<FallbackBindingsObject>("Qt.test.fallbackBindingsDerived", 1, 0, fallback_bindings_derived);
+
+ qmlRegisterType<FallbackBindingsTypeObject>("Qt.test.fallbackBindingsObject", 1, 0, "FallbackBindingsType");
+ qmlRegisterType<FallbackBindingsTypeDerived>("Qt.test.fallbackBindingsDerived", 1, 0, "FallbackBindingsType");
}
#include "testtypes.moc"
MyEnum m_ev;
};
+class FallbackBindingsObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY (int test READ test NOTIFY testChanged)
+public:
+ FallbackBindingsObject(QObject* parent = 0)
+ : QObject(parent), m_test(100)
+ {
+ }
+
+ int test() const { return m_test; }
+
+Q_SIGNALS:
+ void testChanged();
+
+private:
+ int m_test;
+};
+
+class FallbackBindingsDerived : public FallbackBindingsObject
+{
+ Q_OBJECT
+ Q_PROPERTY (QString test READ test NOTIFY testChanged)
+public:
+ FallbackBindingsDerived(QObject* parent = 0)
+ : FallbackBindingsObject(parent), m_test("hello")
+ {
+ }
+
+ QString test() const { return m_test; }
+
+Q_SIGNALS:
+ void testChanged();
+
+private:
+ QString m_test;
+};
+
+class FallbackBindingsAttachedObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY (int test READ test NOTIFY testChanged)
+public:
+ FallbackBindingsAttachedObject(QObject *parent) : QObject(parent), m_test(100) {}
+
+ int test() const { return m_test; }
+
+Q_SIGNALS:
+ void testChanged();
+
+private:
+ int m_test;
+};
+
+class FallbackBindingsAttachedDerived : public FallbackBindingsAttachedObject
+{
+ Q_OBJECT
+ Q_PROPERTY (QString test READ test NOTIFY testChanged)
+public:
+ FallbackBindingsAttachedDerived(QObject* parent = 0)
+ : FallbackBindingsAttachedObject(parent), m_test("hello")
+ {
+ }
+
+ QString test() const { return m_test; }
+
+Q_SIGNALS:
+ void testChanged();
+
+private:
+ QString m_test;
+};
+
+class FallbackBindingsTypeObject : public QObject
+{
+ Q_OBJECT
+public:
+ FallbackBindingsTypeObject() : QObject() {}
+
+ static FallbackBindingsAttachedObject *qmlAttachedProperties(QObject *o) {
+ return new FallbackBindingsAttachedObject(o);
+ }
+};
+
+class FallbackBindingsTypeDerived : public QObject
+{
+ Q_OBJECT
+public:
+ FallbackBindingsTypeDerived() : QObject() {}
+
+ static FallbackBindingsAttachedObject *qmlAttachedProperties(QObject *o) {
+ return new FallbackBindingsAttachedDerived(o);
+ }
+};
+
+QML_DECLARE_TYPEINFO(FallbackBindingsTypeObject, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPEINFO(FallbackBindingsTypeDerived, QML_HAS_ATTACHED_PROPERTIES)
+
void registerTypes();
#endif // TESTTYPES_H
void secondAlias();
void varAlias();
void overrideDataAssert();
+ void fallbackBindings_data();
+ void fallbackBindings();
private:
static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
delete object;
}
+void tst_qqmlecmascript::fallbackBindings_data()
+{
+ QTest::addColumn<QString>("source");
+
+ QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
+ QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
+ QTest::newRow("ModuleAPI without fallback") << "fallbackBindings.3.qml";
+ QTest::newRow("ModuleAPI fallback") << "fallbackBindings.4.qml";
+ QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
+ QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
+}
+
+void tst_qqmlecmascript::fallbackBindings()
+{
+ QFETCH(QString, source);
+
+ QQmlComponent component(&engine, testFileUrl(source));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != 0);
+
+ QCOMPARE(object->property("success").toBool(), true);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"