Merge remote-tracking branch 'origin/5.11' into dev

Change-Id: I4a9c7802c180757e70fa4dd16df3287104a088bc
This commit is contained in:
Qt Forward Merge Bot 2018-04-18 01:00:05 +02:00
commit f0f01cc379
35 changed files with 457 additions and 103 deletions

View File

@ -600,6 +600,7 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen
break;
default:
Q_UNREACHABLE();
return;
}
n->activateMaterial(m_item->window(), gradMat);
if (d->effectiveDirty & DirtyFillGradient) {

View File

@ -784,9 +784,10 @@ bool Codegen::visit(BinaryExpression *ast)
left = left.asLValue();
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
return false;
expression(ast->right).loadInAccumulator();
Reference r = expression(ast->right);
if (hasError)
return false;
r.loadInAccumulator();
if (_expr.accept(nx))
_expr.setResult(left.storeConsumeAccumulator());
else

View File

@ -188,7 +188,7 @@ public:
Const
} type = Invalid;
bool isLValue() const { return !isReadonly; }
bool isLValue() const { return !isReadonly && type > Accumulator; }
Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {}
Reference() {}

View File

@ -257,29 +257,32 @@ struct Context {
usedVariables.insert(name);
}
void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
{
if (! name.isEmpty()) {
if (type != FunctionDefinition) {
for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
if (it->name == name)
return;
}
MemberMap::iterator it = members.find(name);
if (it == members.end()) {
Member m;
m.type = type;
m.function = function;
m.scope = scope;
members.insert(name, m);
} else {
Q_ASSERT(scope == (*it).scope);
if ((*it).type <= type) {
(*it).type = type;
(*it).function = function;
}
}
if (name.isEmpty())
return true;
if (type != FunctionDefinition) {
for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
if (it->name == name)
return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope);
}
MemberMap::iterator it = members.find(name);
if (it != members.end()) {
if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope)
return false;
if ((*it).type <= type) {
(*it).type = type;
(*it).function = function;
}
return true;
}
Member m;
m.type = type;
m.function = function;
m.scope = scope;
members.insert(name, m);
return true;
}
};

View File

@ -215,14 +215,10 @@ bool ScanFunctions::visit(VariableDeclaration *ast)
return false;
}
QString name = ast->name.toString();
const Context::Member *m = nullptr;
if (_context->memberInfo(name, &m)) {
if (m->isLexicallyScoped() || ast->isLexicallyScoped()) {
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
return false;
}
if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
return false;
}
_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope);
return true;
}
@ -397,16 +393,22 @@ bool ScanFunctions::visit(Block *ast) {
void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
{
if (_context) {
_context->hasNestedFunctions = true;
Context *outerContext = _context;
enterEnvironment(ast, FunctionCode);
if (outerContext) {
outerContext->hasNestedFunctions = true;
// The identifier of a function expression cannot be referenced from the enclosing environment.
if (expr)
_context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr);
if (expr) {
if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) {
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
return;
}
}
if (name == QLatin1String("arguments"))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
}
enterEnvironment(ast, FunctionCode);
if (formalsContainName(formals, QStringLiteral("arguments")))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;

View File

@ -297,6 +297,8 @@ qmlRegisterRevision<BaseType,1>("MyTypes", 1, 1);
This is useful when deriving from base classes provided by other authors,
e.g. when extending classes from the Qt Quick module.
\note The QML engine does not support revisions for properties or signals of
grouped and attached property objects.
\section2 Registering Extension Objects

View File

@ -75,11 +75,26 @@ component, which is accessible via QQuickView::rootObject():
\endtable
This \c object is the instance of the \c MyItem.qml component that has been
created. You can now modify the item's properties using QObject::setProperty()
or QQmlProperty:
created. You can now modify the item's properties using
\l QObject::setProperty() or \l QQmlProperty::write():
\snippet qml/qtbinding/loading/main.cpp properties
The difference between \c QObject::setProperty() and \c QQmlProperty::write()
is that the latter will also remove the binding in addition to setting the
property value. For example, suppose the \c width assignment above had been a
binding to \c height:
\code
width: height
\endcode
If the \c height of the \c Item changed after the
\c {object->setProperty("width", 500)} call, the \c width would be updated
again, as the binding remains active. However, if the \c height changes after the
\c {QQmlProperty(object, "width").write(500)} call, the \c width will not be
changed, as the binding does not exist anymore.
Alternatively, you can cast the object to its actual type and call methods with
compile-time safety. In this case the base object of \c MyItem.qml is an
\l Item, which is defined by the QQuickItem class:

View File

@ -96,7 +96,6 @@ void Heap::ArrayBuffer::init(size_t length)
Object::init();
data = QTypedArrayData<char>::allocate(length + 1);
if (!data) {
data = nullptr;
internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
return;
}
@ -113,7 +112,7 @@ void Heap::ArrayBuffer::init(const QByteArray& array)
void Heap::ArrayBuffer::destroy()
{
if (!data->ref.deref())
if (data && !data->ref.deref())
QTypedArrayData<char>::deallocate(data);
Object::destroy();
}

View File

@ -662,14 +662,13 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor)
class ArrayElementLessThan
{
public:
inline ArrayElementLessThan(ExecutionEngine *engine, Object *thisObject, const Value &comparefn)
: m_engine(engine), thisObject(thisObject), m_comparefn(comparefn) {}
inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn)
: m_engine(engine), m_comparefn(comparefn) {}
bool operator()(Value v1, Value v2) const;
private:
ExecutionEngine *m_engine;
Object *thisObject;
const Value &m_comparefn;
};
@ -842,7 +841,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
}
ArrayElementLessThan lessThan(engine, thisObject, static_cast<const FunctionObject &>(comparefn));
ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn));
Value *begin = thisObject->arrayData()->values.values;
sortHelper(begin, begin + len, *begin, lessThan);

View File

@ -75,7 +75,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
jsConstruct = QV4::FunctionObject::callAsConstructor;
Object::init();
function = nullptr;
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
@ -88,7 +87,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
function = nullptr;
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
@ -101,8 +99,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
this->function = function;
function->compilationUnit->addref();
setFunction(function);
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedString name(s, function->name());
@ -123,13 +120,18 @@ void Heap::FunctionObject::init()
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
function = nullptr;
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
}
void Heap::FunctionObject::setFunction(Function *f)
{
if (f) {
function = f;
function->compilationUnit->addref();
}
}
void Heap::FunctionObject::destroy()
{
if (function)
@ -347,20 +349,36 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
{
QV4::Scope scope(b);
ScopedFunctionObject target(scope, thisObject);
if (!target)
if (!target || target->isBinding())
return scope.engine->throwTypeError();
ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue());
Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr);
if (argc > 1) {
boundArgs = MemberData::allocate(scope.engine, argc - 1);
boundArgs->d()->values.size = argc - 1;
for (uint i = 0, ei = static_cast<uint>(argc - 1); i < ei; ++i)
int nArgs = (argc - 1 >= 0) ? argc - 1 : 0;
if (target->isBoundFunction()) {
BoundFunction *bound = static_cast<BoundFunction *>(target.getPointer());
Scoped<MemberData> oldArgs(scope, bound->boundArgs());
boundThis = bound->boundThis();
int oldSize = oldArgs->size();
boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs);
boundArgs->d()->values.size = oldSize + nArgs;
for (uint i = 0; i < static_cast<uint>(oldSize); ++i)
boundArgs->set(scope.engine, i, oldArgs->data()[i]);
for (uint i = 0; i < static_cast<uint>(nArgs); ++i)
boundArgs->set(scope.engine, oldSize + i, argv[i + 1]);
target = bound->target();
} else if (nArgs) {
boundArgs = MemberData::allocate(scope.engine, nArgs);
boundArgs->d()->values.size = nArgs;
for (uint i = 0, ei = static_cast<uint>(nArgs); i < ei; ++i)
boundArgs->set(scope.engine, i, argv[i + 1]);
}
ExecutionContext *global = scope.engine->rootContext();
return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue();
ScopedContext ctx(scope, target->scope());
Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs);
bound->setFunction(target->function());
return bound->asReturnedValue();
}
DEFINE_OBJECT_VTABLE(ScriptFunction);
@ -392,8 +410,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
FunctionObject::init();
this->scope.set(scope->engine(), scope->d());
this->function = function;
function->compilationUnit->addref();
setFunction(function);
Q_ASSERT(function);
Q_ASSERT(function->code);

View File

@ -90,6 +90,8 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
void init();
void destroy();
void setFunction(Function *f);
unsigned int formalParameterCount() { return function ? function->nFormals : 0; }
unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; }

View File

@ -637,7 +637,7 @@ struct Stringify
Stringify(ExecutionEngine *e) : v4(e), replacerFunction(nullptr), propertyList(nullptr), propertyListSize(0) {}
QString Str(const QString &key, const Value &v);
QString JA(ArrayObject *a);
QString JA(Object *a);
QString JO(Object *o);
QString makeMember(const QString &key, const Value &v);
@ -743,8 +743,8 @@ QString Stringify::Str(const QString &key, const Value &v)
o = value->asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
if (o->as<ArrayObject>()) {
return JA(static_cast<ArrayObject *>(o.getPointer()));
if (o->as<ArrayObject>() || o->isListType()) {
return JA(o.getPointer());
} else {
return JO(o);
}
@ -827,7 +827,7 @@ QString Stringify::JO(Object *o)
return result;
}
QString Stringify::JA(ArrayObject *a)
QString Stringify::JA(Object *a)
{
if (stackContains(a)) {
v4->throwTypeError();

View File

@ -1092,9 +1092,7 @@ bool Object::setArrayLength(uint newLen)
uint oldLen = getLength();
bool ok = true;
if (newLen < oldLen) {
if (!arrayData()) {
Q_ASSERT(!newLen);
} else {
if (arrayData()) {
uint l = arrayData()->vtable()->truncate(this, newLen);
if (l != newLen)
ok = false;

View File

@ -466,9 +466,12 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
QV4::ScopedContext ctx(scope, bindingFunction->scope());
newBinding = QQmlBinding::create(property, bindingFunction->function(), object, callingQmlContext, ctx);
newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
if (f->isBoundFunction())
newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
newBinding->setTarget(object, *property, nullptr);
}
}

View File

@ -1271,7 +1271,9 @@ QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
return engine->qmlContext()->asReturnedValue();
Heap::QmlContext *ctx = engine->qmlContext();
Q_ASSERT(ctx);
return ctx->asReturnedValue();
}
ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)

View File

@ -51,6 +51,7 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4jscall_p.h>
#include <QVariant>
#include <QtCore/qdebug.h>
@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
return b;
}
QQmlSourceLocation QQmlBinding::sourceLocation() const
{
if (m_sourceLocation)
return *m_sourceLocation;
return QQmlJavaScriptExpression::sourceLocation();
}
void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
{
if (m_sourceLocation)
delete m_sourceLocation;
m_sourceLocation = new QQmlSourceLocation(location);
}
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
{
@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function
QQmlBinding::~QQmlBinding()
{
delete m_sourceLocation;
}
void QQmlBinding::setNotifyOnValueChanged(bool v)
@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
setUpdatingFlag(false);
}
QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
{
QV4::ExecutionEngine *v4 = context()->engine->handle();
int argc = 0;
const QV4::Value *argv = nullptr;
const QV4::Value *thisObject = nullptr;
QV4::BoundFunction *b = nullptr;
if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
QV4::Heap::MemberData *args = b->boundArgs();
if (args) {
argc = args->values.size;
argv = args->values.data();
}
thisObject = &b->d()->boundThis;
}
QV4::Scope scope(v4);
QV4::JSCallData jsCall(scope, argc, argv, thisObject);
return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined);
}
// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
// expression for the switch for the compiler to generate the optimal code, but
@ -203,7 +242,7 @@ protected:
bool isUndefined = false;
QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
QV4::ScopedValue result(scope, evaluate(&isUndefined));
bool error = false;
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
@ -302,9 +341,14 @@ public:
{
setCompilationUnit(compilationUnit);
m_binding = binding;
setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column));
}
QQmlSourceLocation sourceLocation() const override final
{
return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
}
void doUpdate(const DeleteWatcher &watcher,
QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
{

View File

@ -104,6 +104,12 @@ public:
QString expressionIdentifier() const override;
void expressionChanged() override;
QQmlSourceLocation sourceLocation() const override;
void setSourceLocation(const QQmlSourceLocation &location);
void setBoundFunction(QV4::BoundFunction *boundFunction) {
m_boundFunction.set(boundFunction->engine(), *boundFunction);
}
/**
* This method returns a snapshot of the currently tracked dependencies of
* this binding. The dependencies can change upon reevaluation. This method is
@ -123,6 +129,8 @@ protected:
bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags);
QV4::ReturnedValue evaluate(bool *isUndefined);
private:
inline bool updatingFlag() const;
inline void setUpdatingFlag(bool);
@ -130,6 +138,9 @@ private:
inline void setEnabledFlag(bool);
static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property);
QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions
QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object
};
bool QQmlBinding::updatingFlag() const

View File

@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression()
m_context(nullptr),
m_prevExpression(nullptr),
m_nextExpression(nullptr),
m_v4Function(nullptr),
m_sourceLocation(nullptr)
m_v4Function(nullptr)
{
}
@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = nullptr;
delete m_sourceLocation;
}
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const
{
if (m_sourceLocation)
return *m_sourceLocation;
if (m_v4Function)
return m_v4Function->sourceLocation();
return QQmlSourceLocation();
}
void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location)
{
if (m_sourceLocation)
delete m_sourceLocation;
m_sourceLocation = new QQmlSourceLocation(location);
}
void QQmlJavaScriptExpression::setContext(QQmlContextData *context)
{
if (m_prevExpression) {

View File

@ -116,8 +116,7 @@ public:
inline QObject *scopeObject() const;
inline void setScopeObject(QObject *v);
QQmlSourceLocation sourceLocation() const;
void setSourceLocation(const QQmlSourceLocation &location);
virtual QQmlSourceLocation sourceLocation() const;
bool isValid() const { return context() != nullptr; }
@ -188,7 +187,6 @@ private:
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
QV4::Function *m_v4Function;
QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions
};
class QQmlPropertyCapture

View File

@ -388,6 +388,7 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value
QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
CompiledData::CompilationUnit *cu = td->compilationUnit();
myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
td->release();
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId);
}

View File

@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
QV4::ScopedContext ctx(scope, bindingFunction->scope());
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx);
QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
QV4::ScopedContext ctx(scope, f->scope());
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
if (f->isBoundFunction())
newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
newBinding->setSourceLocation(bindingFunction->currentLocation());
newBinding->setTarget(referenceObject, cacheData, pd);
QQmlPropertyPrivate::setBinding(newBinding);

View File

@ -1350,11 +1350,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co
}
#endif
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction)
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
{
Scope scope(originalFunction->engine());
ScopedContext context(scope, originalFunction->scope());
FunctionObject::init(context, originalFunction->function());
Scope scope(bindingFunction->engine());
ScopedContext context(scope, bindingFunction->scope());
FunctionObject::init(context, bindingFunction->function());
this->bindingFunction.set(internalClass->engine, bindingFunction->d());
}
QQmlSourceLocation QQmlBindingFunction::currentLocation() const

View File

@ -80,8 +80,11 @@ struct ConsoleObject : Object {
void init();
};
struct QQmlBindingFunction : FunctionObject {
void init(const QV4::FunctionObject *originalFunction);
#define QQmlBindingFunctionMembers(class, Member) \
Member(class, Pointer, FunctionObject *, bindingFunction)
DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) {
DECLARE_MARKOBJECTS(QQmlBindingFunction)
void init(const QV4::FunctionObject *bindingFunction);
};
}
@ -181,6 +184,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject
{
V4_OBJECT2(QQmlBindingFunction, FunctionObject)
Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; }
QQmlSourceLocation currentLocation() const; // from caller stack trace
};

View File

@ -182,4 +182,13 @@
#include "tst_mytest.moc"
\endcode
\section1 Licenses
Qt Quick Tests is available under commercial licenses from \l{The Qt Company}.
In addition, it is available under free software licenses. Since Qt 5.4,
these free software licenses are
\l{GNU Lesser General Public License, version 3}, or
the \l{GNU General Public License, version 2}.
See \l{Qt Licensing} for further details.
*/

View File

@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
//! [ext]
// Assuming the "pics" directory contains the following files:
// dog.jpg
// cat.png
// cat.pkm
Image {
source: "pics/cat.png" // loads cat.png
}
Image {
source: "pics/dog" // loads dog.jpg
}
Image {
source: "pics/cat" // normally loads cat.pkm, but if no OpenGL, loads cat.png instead.
}
//! [ext]

View File

@ -39,6 +39,15 @@
Visit the \l{Qt Quick Layouts Overview} page to get started.
\section1 Licenses
Qt Quick Layouts is available under commercial licenses from \l{The Qt Company}.
In addition, it is available under free software licenses. Since Qt 5.4,
these free software licenses are
\l{GNU Lesser General Public License, version 3}, or
the \l{GNU General Public License, version 2}.
See \l{Qt Licensing} for further details.
\section1 Layouts
\annotatedlist layouts

View File

@ -135,6 +135,48 @@ QQuickImagePrivate::QQuickImagePrivate()
\clearfloat
\section1 OpenGL Texture Files
When the default OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in
use, images can also be supplied in compressed texture files. The content
must be a simple RGB(A) format 2D texture. Supported compression schemes
are only limited by the underlying OpenGL driver and GPU. The following
container file formats are supported:
\list
\li \c PKM (since Qt 5.10)
\li \c KTX (since Qt 5.11)
\endlist
\note Semi-transparent original images require alpha pre-multiplication
prior to texture compression in order to be correctly displayed in Qt
Quick. This can be done with the following ImageMagick command
line:
\badcode
convert MYORIGIMAGE \( +clone -alpha Extract \) -channel RGB -compose Multiply -composite MYPMIMAGE
\endcode
\section1 Automatic Detection of File Extension
If the \l source URL indicates a non-existing local file or resource, the
Image element attempts to auto-detect the file extension. If an existing
file can be found by appending any of the supported image file extensions
to the \l source URL, then that file will be loaded.
If the OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in use, the
file search the attempts the OpenGL texture file extensions first. If the
search is unsuccessful, it attempts to search with the file extensions for
the \l{QImageReader::supportedImageFormats()}{conventional image file
types}. For example:
\snippet qml/image-ext.qml ext
This functionality facilitates deploying different image asset file types
on different target platforms. This can be useful in order to tune
application performance and adapt to different graphics hardware.
This functionality was introduced in Qt 5.11.
\section1 Performance
By default, locally available images are loaded immediately, and the user interface
@ -154,7 +196,7 @@ QQuickImagePrivate::QQuickImagePrivate()
size bounded via the \l sourceSize property. This is especially important for content
that is loaded from external sources or provided by the user.
\sa {Qt Quick Examples - Image Elements}, QQuickImageProvider
\sa {Qt Quick Examples - Image Elements}, QQuickImageProvider, QImageReader::setAutoDetectImageFormat()
*/
QQuickImage::QQuickImage(QQuickItem *parent)
@ -461,7 +503,7 @@ qreal QQuickImage::paintedHeight() const
The URL may be absolute, or relative to the URL of the component.
\sa QQuickImageProvider
\sa QQuickImageProvider {OpenGL Texture Files} {Automatic Detection of File Extension}
*/
/*!

View File

@ -2318,8 +2318,10 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
if (newGeometry.height() > oldGeometry.height()) {
if (!d->heightExceeded) // Height is adequate and growing.
if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
// Height is adequate and growing, and it wasn't 0 previously.
goto geomChangeDone;
}
if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
goto geomChangeDone;
} else if (newGeometry.height() < oldGeometry.height()) {

View File

@ -129,6 +129,7 @@ private slots:
void arraySort();
void lookupOnDisappearingProperty();
void arrayConcat();
void recursiveBoundFunctions();
void qRegExpInport_data();
void qRegExpInport();
@ -3025,6 +3026,18 @@ void tst_QJSEngine::arrayConcat()
QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12"));
}
void tst_QJSEngine::recursiveBoundFunctions()
{
QJSEngine eng;
QJSValue v = eng.evaluate("function foo(x, y, z)"
"{ return this + x + y + z; }"
"var bar = foo.bind(-1, 10);"
"var baz = bar.bind(-2, 20);"
"baz(30)");
QCOMPARE(v.toInt(), 59);
}
static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
void tst_QJSEngine::qRegExpInport_data()

View File

@ -0,0 +1,34 @@
import QtQuick 2.6
QtObject {
property bool success: false
property var num: 100
property var simple: 0
property var complex: 0
Component.onCompleted: {
function s(x) {
return x
}
function c(x) {
return x + num
}
var bound = s.bind(undefined, 100)
simple = Qt.binding(bound)
if (simple != 100)
return;
var bound = c.bind(undefined, 100)
complex = Qt.binding(bound);
if (complex != 200)
return;
num = 0;
if (complex != 100)
return;
print("success!!!");
success = true;
}
}

View File

@ -10,6 +10,11 @@ Item {
objectName: "msco"
}
Component {
id: mscoComponent
MySequenceConversionObject { }
}
property bool success: false
property variant intList
@ -252,4 +257,13 @@ Item {
if (testSequence.valueOf() == prevValueOf) referenceDeletion = false;
if (testSequence.length == prevLength) referenceDeletion = false;
}
function jsonConversion() {
success = true
var msco = mscoComponent.createObject()
if (JSON.stringify(msco.intListProperty) != "[1,2,3,4]") success = false;
if (JSON.stringify(msco.qrealListProperty) != "[1.1,2.2,3.3,4.4]") success = false;
if (JSON.stringify(msco.boolListProperty) != "[true,false,true,false]") success = false;
if (JSON.stringify(msco.stringListProperty) != "[\"first\",\"second\",\"third\",\"fourth\"]") success = false;
}
}

View File

@ -289,6 +289,7 @@ private slots:
void withStatement();
void tryStatement();
void replaceBinding();
void bindingBoundFunctions();
void deleteRootObjectInCreation();
void onDestruction();
void onDestructionViaGC();
@ -5562,17 +5563,18 @@ void tst_qqmlecmascript::sequenceConversionArray()
// ensure that in JS the returned sequences act just like normal JS Arrays.
QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
QQmlComponent component(&engine, qmlFile);
QObject *object = component.create();
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QMetaObject::invokeMethod(object, "indexedAccess");
QMetaObject::invokeMethod(object.data(), "indexedAccess");
QVERIFY(object->property("success").toBool());
QMetaObject::invokeMethod(object, "arrayOperations");
QMetaObject::invokeMethod(object.data(), "arrayOperations");
QVERIFY(object->property("success").toBool());
QMetaObject::invokeMethod(object, "testEqualitySemantics");
QMetaObject::invokeMethod(object.data(), "testEqualitySemantics");
QVERIFY(object->property("success").toBool());
QMetaObject::invokeMethod(object, "testReferenceDeletion");
QMetaObject::invokeMethod(object.data(), "testReferenceDeletion");
QCOMPARE(object->property("referenceDeletion").toBool(), true);
delete object;
QMetaObject::invokeMethod(object.data(), "jsonConversion");
QVERIFY(object->property("success").toBool());
}
@ -7246,6 +7248,17 @@ void tst_qqmlecmascript::replaceBinding()
delete obj;
}
void tst_qqmlecmascript::bindingBoundFunctions()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("bindingBoundFunctions.qml"));
QObject *obj = c.create();
QVERIFY(obj != nullptr);
QVERIFY(obj->property("success").toBool());
delete obj;
}
void tst_qqmlecmascript::deleteRootObjectInCreation()
{
{

View File

@ -86,6 +86,7 @@ tst_examples::tst_examples()
excludedDirs << "snippets/qml/visualdatamodel_rootindex";
excludedDirs << "snippets/qml/qtbinding";
excludedDirs << "snippets/qml/imports";
excludedFiles << "snippets/qml/image-ext.qml";
excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources
excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources

View File

@ -0,0 +1,13 @@
import QtQuick 2.0
Item {
width: 100
height: 30
Text {
width: parent ? parent.width : 0
height: parent ? parent.height : 0
elide: Text.ElideRight
text: "wot"
}
}

View File

@ -38,6 +38,7 @@
#include <QFontMetrics>
#include <qmath.h>
#include <QtQuick/QQuickView>
#include <QtQuick/qquickitemgrabresult.h>
#include <private/qguiapplication_p.h>
#include <limits.h>
#include <QtGui/QMouseEvent>
@ -64,6 +65,7 @@ private slots:
void width();
void wrap();
void elide();
void elideParentChanged();
void multilineElide_data();
void multilineElide();
void implicitElide_data();
@ -554,6 +556,45 @@ void tst_qquicktext::elide()
}
}
// QTBUG-60328
// Tests that text with elide set is rendered after
// having its parent cleared and then set again.
void tst_qquicktext::elideParentChanged()
{
QQuickView window;
window.setSource(testFileUrl("elideParentChanged.qml"));
QTRY_COMPARE(window.status(), QQuickView::Ready);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QCOMPARE(root->childItems().size(), 1);
// Store a snapshot of the scene so that we can compare it later.
QSharedPointer<QQuickItemGrabResult> grabResult = root->grabToImage();
QTRY_VERIFY(!grabResult->image().isNull());
const QImage expectedItemImageGrab(grabResult->image());
// Clear the text's parent. It shouldn't render anything.
QQuickItem *text = root->childItems().first();
text->setParentItem(nullptr);
QCOMPARE(text->width(), 0.0);
QCOMPARE(text->height(), 0.0);
// Set the parent back to what it was. The text should
// be rendered identically to how it was before.
text->setParentItem(root);
QCOMPARE(text->width(), 100.0);
QCOMPARE(text->height(), 30.0);
grabResult = root->grabToImage();
QTRY_VERIFY(!grabResult->image().isNull());
const QImage actualItemImageGrab(grabResult->image());
QCOMPARE(actualItemImageGrab, expectedItemImageGrab);
}
void tst_qquicktext::multilineElide_data()
{
QTest::addColumn<QQuickText::TextFormat>("format");