6#include <private/qqmlbinding_p.h>
7#include <private/qqmlglobal_p.h>
8#include <private/qqmlscriptstring_p.h>
9#include <private/qv4functionobject_p.h>
10#include <private/qv4jscall_p.h>
11#include <private/qv4qmlcontext_p.h>
13#include <QtQml/qqmlinfo.h>
15#include <QtCore/qloggingcategory.h>
19using namespace Qt::Literals::StringLiterals;
23QUntypedPropertyBinding QQmlPropertyBinding::create(
const QQmlPropertyData *pd, QV4::Function *function,
24 QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt,
25 QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
28 return create(pd->propType(), function, obj, ctxt, scope, target, targetIndex);
31QUntypedPropertyBinding QQmlPropertyBinding::create(QMetaType propertyType, QV4::Function *function,
33 const QQmlRefPointer<QQmlContextData> &ctxt,
34 QV4::ExecutionContext *scope, QObject *target,
35 QQmlPropertyIndex targetIndex)
37 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
38 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
39 auto binding =
new (buffer) QQmlPropertyBinding(propertyType, target, targetIndex,
40 TargetData::WithoutBoundFunction);
41 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
42 Q_ASSERT(binding->jsExpression() == js);
43 Q_ASSERT(js->asBinding() == binding);
45 binding->jsExpression()->setNotifyOnValueChanged(
true);
46 binding->jsExpression()->setContext(ctxt);
47 binding->jsExpression()->setScopeObject(obj);
48 binding->jsExpression()->setupFunction(scope, function);
49 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
52QUntypedPropertyBinding QQmlPropertyBinding::createFromCodeString(
const QQmlPropertyData *pd,
const QString& str, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt,
const QString &url, quint16 lineNumber, QObject *target, QQmlPropertyIndex targetIndex)
54 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
55 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
56 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
57 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
58 Q_ASSERT(binding->jsExpression() == js);
59 Q_ASSERT(js->asBinding() == binding);
61 binding->jsExpression()->setNotifyOnValueChanged(
true);
62 binding->jsExpression()->setContext(ctxt);
63 binding->jsExpression()->createQmlBinding(ctxt, obj, str, url, lineNumber);
64 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
67QUntypedPropertyBinding QQmlPropertyBinding::createFromScriptString(
const QQmlPropertyData *property,
const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt, QObject *target, QQmlPropertyIndex targetIndex)
69 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
71 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) {
75 auto scopeObject = obj ? obj : scriptPrivate->scope;
77 QV4::Function *runtimeFunction =
nullptr;
79 QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context);
80 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
81 if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) {
82 url = ctxtdata->urlString();
83 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
84 runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId);
88 return createFromCodeString(property, scriptPrivate->script, obj, ctxtdata, url, scriptPrivate->lineNumber, target, targetIndex);
90 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
91 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
92 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(property->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
93 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
94 Q_ASSERT(binding->jsExpression() == js);
95 Q_ASSERT(js->asBinding() == binding);
96 js->setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
98 QV4::ExecutionEngine *v4 = engine->v4engine();
100 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, scopeObject));
101 js->setupFunction(qmlContext, runtimeFunction);
102 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
105QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(
const QQmlPropertyData *pd, QV4::BoundFunction *function, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
107 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
108 +
sizeof(QQmlPropertyBindingJSForBoundFunction)+jsExpressionOffsetLength()];
109 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::HasBoundFunction);
110 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJSForBoundFunction();
111 Q_ASSERT(binding->jsExpression() == js);
112 Q_ASSERT(js->asBinding() == binding);
114 binding->jsExpression()->setNotifyOnValueChanged(
true);
115 binding->jsExpression()->setContext(ctxt);
116 binding->jsExpression()->setScopeObject(obj);
117 binding->jsExpression()->setupFunction(scope, function->function());
118 js->m_boundFunction.set(function->engine(), *function);
119 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
123
124
125
126
127
128
129
130
131
134void QQmlPropertyBindingJS::expressionChanged()
136 auto binding = asBinding();
137 if (!binding->propertyDataPtr)
139 const auto currentTag = m_error.tag();
140 if (currentTag == InEvaluationLoop) {
142 auto location = QQmlJavaScriptExpression::sourceLocation();
143 err.setUrl(QUrl{location.sourceFile});
144 err.setLine(location.line);
145 err.setColumn(location.column);
146 const auto ctxt = context();
147 QQmlEngine *engine = ctxt ? ctxt->engine() :
nullptr;
149 err.setDescription(asBinding()->createBindingLoopErrorDescription());
151 err.setDescription(QString::fromLatin1(
"Binding loop detected"));
152 err.setObject(asBinding()->target());
153 qmlWarning(
this->scopeObject(), err);
156 m_error.setTag(InEvaluationLoop);
157 PendingBindingObserverList bindingObservers;
158 binding->evaluateRecursive(bindingObservers);
159 binding->notifyNonRecursive(bindingObservers);
160 m_error.setTag(NoTag);
163QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction)
164 : QPropertyBindingPrivate(mt,
165 bindingFunctionVTableForQQmlPropertyBinding(mt),
166 QPropertyBindingSourceLocation(),
true)
168 static_assert (std::is_trivially_destructible_v<TargetData>);
169 static_assert (
sizeof(TargetData) +
sizeof(DeclarativeErrorCallback) <=
sizeof(QPropertyBindingSourceLocation));
170 static_assert (
alignof(TargetData) <=
alignof(QPropertyBindingSourceLocation));
171 const auto state = hasBoundFunction ? TargetData::HasBoundFunction : TargetData::WithoutBoundFunction;
172 new (&declarativeExtraData) TargetData {target, targetIndex, state};
173 errorCallBack = bindingErrorCallback;
180
181
182
183
184
185
187 std::byte *qpropertyPointer =
reinterpret_cast<
std::byte *>(dataPtr);
188 qpropertyPointer += type.sizeOf();
189 constexpr auto alignment =
alignof(QtPrivate::QPropertyBindingData *);
190 auto aligned = (quintptr(qpropertyPointer) + alignment - 1) & ~(alignment - 1);
191 return reinterpret_cast<QtPrivate::QPropertyBindingData *>(aligned);
194void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep,
void *dataPtr)
196 const QQmlPropertyData *propertyData =
nullptr;
197 QQmlPropertyData valueTypeData;
198 QQmlData *data = QQmlData::get(target(),
false);
200 if (Q_UNLIKELY(!data->propertyCache))
201 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
203 propertyData = data->propertyCache->property(targetIndex().coreIndex());
204 Q_ASSERT(propertyData);
205 Q_ASSERT(!targetIndex().hasValueTypeIndex());
206 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
210 auto writeBackCurrentValue = [&](QVariant &¤tValue) {
211 if (currentValue.metaType() != valueMetaType())
212 currentValue.convert(valueMetaType());
213 auto metaType = valueMetaType();
214 metaType.destruct(dataPtr);
215 metaType.construct(dataPtr, currentValue.constData());
217 if (prop.isResettable()) {
221 const auto storage = qGetBindingStorage(target());
222 auto bindingData = storage->bindingData(propertyDataPtr);
224 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
225 QPropertyBindingDataPointer bindingDataPointer{bindingData};
226 auto firstObserver = takeObservers();
227 bindingData->d_ref() = 0;
229 bindingDataPointer.setObservers(firstObserver.ptr);
231 Q_ASSERT(!bindingData->hasBinding());
232 setIsUndefined(
true);
234 auto state = QtPrivate::suspendCurrentBindingStatus();
236 QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
237 QtPrivate::restoreBindingStatus(state);
238 writeBackCurrentValue(std::move(currentValue));
241 bindingData = storage->bindingData(propertyDataPtr);
243 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
244 bindingDataPointer = QPropertyBindingDataPointer {bindingData};
247 if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
248 qCWarning(lcQQPropertyBinding) <<
"Resetting " << prop.name() <<
"due to the binding becoming undefined caused a new binding to be installed\n"
249 <<
"The old binding binding will be abandoned";
254 firstObserver = bindingDataPointer.firstObserver();
255 bindingData->d_ref() =
reinterpret_cast<quintptr>(
this) | QtPrivate::QPropertyBindingData::BindingBit;
257 prependObserver(firstObserver);
260 auto location = jsExpression()->sourceLocation();
261 qmlError.setColumn(location.column);
262 qmlError.setLine(location.line);
263 qmlError.setUrl(QUrl {location.sourceFile});
264 const QString description = QStringLiteral(R"(QML %1: Unable to assign [undefined] to "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
265 qmlError.setDescription(description);
266 qmlError.setObject(target());
267 ep->warning(qmlError);
271QString QQmlPropertyBinding::createBindingLoopErrorDescription()
273 const QQmlPropertyData *propertyData =
nullptr;
274 QQmlPropertyData valueTypeData;
275 QQmlData *data = QQmlData::get(target(),
false);
277 if (Q_UNLIKELY(!data->propertyCache))
278 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
280 propertyData = data->propertyCache->property(targetIndex().coreIndex());
281 Q_ASSERT(propertyData);
282 Q_ASSERT(!targetIndex().hasValueTypeIndex());
283 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
284 return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
287void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
289 auto This =
static_cast<QQmlPropertyBinding *>(that);
290 auto target = This->target();
291 auto engine = qmlEngine(target);
295 auto error = This->bindingError();
297 auto location = This->jsExpression()->sourceLocation();
298 qmlError.setColumn(location.column);
299 qmlError.setLine(location.line);
300 qmlError.setUrl(QUrl {location.sourceFile});
301 auto description = error.description();
302 if (error.type() == QPropertyBindingError::BindingLoop) {
303 description = This->createBindingLoopErrorDescription();
305 qmlError.setDescription(description);
306 qmlError.setObject(target);
307 QQmlEnginePrivate::get(engine)->warning(qmlError);
310template<
typename TranslateWithUnit>
312 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
313 TranslateWithUnit &&translateWithUnit)
315 return [compilationUnit, translateWithUnit](QMetaType metaType,
void *dataPtr) ->
bool {
317 QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
319 QVariant resultVariant(translateWithUnit(compilationUnit));
320 if (metaType != QMetaType::fromType<QString>())
321 resultVariant.convert(metaType);
323 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
324 metaType.destruct(dataPtr);
325 metaType.construct(dataPtr, resultVariant.constData());
331 const QQmlPropertyData *pd,
332 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
333 const QV4::CompiledData::Binding *binding)
335 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
337 [binding](
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
338 return compilationUnit->bindingValueAsString(binding);
341 return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
342 QPropertyBindingSourceLocation());
346 const QMetaType &propertyType,
347 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
348 const QQmlTranslation &translationData)
350 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
353 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
354 Q_UNUSED(compilationUnit);
355 return translationData.translate();
358 return QUntypedPropertyBinding(propertyType, translationBinding,
359 QPropertyBindingSourceLocation());
362QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(
bool *isUndefined)
364 QV4::ExecutionEngine *v4 = engine()->handle();
366 const QV4::Value *argv =
nullptr;
367 const QV4::Value *thisObject =
nullptr;
368 QV4::BoundFunction *b =
nullptr;
369 if ((b = m_boundFunction.as<QV4::BoundFunction>())) {
370 QV4::Heap::MemberData *args = b->boundArgs();
372 argc = args->values.size;
373 argv = args->values.data();
375 thisObject = &b->d()->boundThis;
377 QV4::Scope scope(v4);
378 QV4::JSCallData jsCall(thisObject, argv, argc);
380 return QQmlJavaScriptExpression::evaluate(jsCall.callData(scope), isUndefined);
Combined button and popup list for selecting options.
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
static QtPrivate::QPropertyBindingData * bindingDataFromPropertyData(QUntypedPropertyData *dataPtr, QMetaType type)
auto qQmlTranslationPropertyBindingCreateBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, TranslateWithUnit &&translateWithUnit)