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.get();
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 if (!hasValidContext())
139 auto binding = asBinding();
140 if (!binding->propertyDataPtr)
142 const auto currentTag = m_error.tag();
143 if (currentTag == InEvaluationLoop) {
145 auto location = QQmlJavaScriptExpression::sourceLocation();
146 err.setUrl(QUrl{location.sourceFile});
147 err.setLine(location.line);
148 err.setColumn(location.column);
149 const auto ctxt = context();
150 QQmlEngine *engine = ctxt ? ctxt->engine() :
nullptr;
152 err.setDescription(asBinding()->createBindingLoopErrorDescription());
154 err.setDescription(QString::fromLatin1(
"Binding loop detected"));
155 err.setObject(asBinding()->target());
156 qmlWarning(
this->scopeObject(), err);
159 m_error.setTag(InEvaluationLoop);
160 PendingBindingObserverList bindingObservers;
161 binding->evaluateRecursive(bindingObservers);
162 binding->notifyNonRecursive(bindingObservers);
163 m_error.setTag(NoTag);
166QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction)
167 : QPropertyBindingPrivate(mt,
168 bindingFunctionVTableForQQmlPropertyBinding(mt),
169 QPropertyBindingSourceLocation(),
true)
171 static_assert (std::is_trivially_destructible_v<TargetData>);
172 static_assert (
sizeof(TargetData) +
sizeof(DeclarativeErrorCallback) <=
sizeof(QPropertyBindingSourceLocation));
173 static_assert (
alignof(TargetData) <=
alignof(QPropertyBindingSourceLocation));
174 const auto state = hasBoundFunction ? TargetData::HasBoundFunction : TargetData::WithoutBoundFunction;
175 new (&declarativeExtraData) TargetData {target, targetIndex, state};
176 errorCallBack = bindingErrorCallback;
183
184
185
186
187
188
190 std::byte *qpropertyPointer =
reinterpret_cast<
std::byte *>(dataPtr);
191 qpropertyPointer += type.sizeOf();
192 constexpr auto alignment =
alignof(QtPrivate::QPropertyBindingData *);
193 auto aligned = (quintptr(qpropertyPointer) + alignment - 1) & ~(alignment - 1);
194 return reinterpret_cast<QtPrivate::QPropertyBindingData *>(aligned);
197void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep,
void *dataPtr)
199 const QQmlPropertyData *propertyData =
nullptr;
200 QQmlPropertyData valueTypeData;
201 QQmlData *data = QQmlData::get(target(),
false);
203 if (Q_UNLIKELY(!data->propertyCache))
204 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
206 propertyData = data->propertyCache->property(targetIndex().coreIndex());
207 Q_ASSERT(propertyData);
208 Q_ASSERT(!targetIndex().hasValueTypeIndex());
209 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
213 auto writeBackCurrentValue = [&](QVariant &¤tValue) {
214 if (currentValue.metaType() != valueMetaType())
215 currentValue.convert(valueMetaType());
216 auto metaType = valueMetaType();
217 metaType.destruct(dataPtr);
218 metaType.construct(dataPtr, currentValue.constData());
220 if (prop.isResettable()) {
224 const auto storage = qGetBindingStorage(target());
225 auto bindingData = storage->bindingData(propertyDataPtr);
227 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
228 QPropertyBindingDataPointer bindingDataPointer{bindingData};
229 auto firstObserver = takeObservers();
230 bindingData->d_ref() = 0;
232 bindingDataPointer.setObservers(firstObserver.ptr);
234 Q_ASSERT(!bindingData->hasBinding());
235 setIsUndefined(
true);
237 auto state = QtPrivate::suspendCurrentBindingStatus();
239 QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
240 QtPrivate::restoreBindingStatus(state);
241 writeBackCurrentValue(std::move(currentValue));
244 bindingData = storage->bindingData(propertyDataPtr);
246 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
247 bindingDataPointer = QPropertyBindingDataPointer {bindingData};
250 if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
251 qCWarning(lcQQPropertyBinding) <<
"Resetting " << prop.name() <<
"due to the binding becoming undefined caused a new binding to be installed\n"
252 <<
"The old binding binding will be abandoned";
257 firstObserver = bindingDataPointer.firstObserver();
258 bindingData->d_ref() =
reinterpret_cast<quintptr>(
this) | QtPrivate::QPropertyBindingData::BindingBit;
260 prependObserver(firstObserver);
263 auto location = jsExpression()->sourceLocation();
264 qmlError.setColumn(location.column);
265 qmlError.setLine(location.line);
266 qmlError.setUrl(QUrl {location.sourceFile});
267 const QString description = QStringLiteral(R"(QML %1: Unable to assign [undefined] to "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
268 qmlError.setDescription(description);
269 qmlError.setObject(target());
270 ep->warning(qmlError);
274QString QQmlPropertyBinding::createBindingLoopErrorDescription()
276 const QQmlPropertyData *propertyData =
nullptr;
277 QQmlPropertyData valueTypeData;
278 QQmlData *data = QQmlData::get(target(),
false);
280 if (Q_UNLIKELY(!data->propertyCache))
281 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
283 propertyData = data->propertyCache->property(targetIndex().coreIndex());
284 Q_ASSERT(propertyData);
285 Q_ASSERT(!targetIndex().hasValueTypeIndex());
286 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
287 return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
290void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
292 auto This =
static_cast<QQmlPropertyBinding *>(that);
293 auto target = This->target();
294 auto engine = qmlEngine(target);
298 auto error = This->bindingError();
300 auto location = This->jsExpression()->sourceLocation();
301 qmlError.setColumn(location.column);
302 qmlError.setLine(location.line);
303 qmlError.setUrl(QUrl {location.sourceFile});
304 auto description = error.description();
305 if (error.type() == QPropertyBindingError::BindingLoop) {
306 description = This->createBindingLoopErrorDescription();
308 qmlError.setDescription(description);
309 qmlError.setObject(target);
310 QQmlEnginePrivate::get(engine)->warning(qmlError);
313template<
typename TranslateWithUnit>
315 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
316 TranslateWithUnit &&translateWithUnit)
318 return [compilationUnit, translateWithUnit](QMetaType metaType,
void *dataPtr) ->
bool {
320 QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
322 QVariant resultVariant(translateWithUnit(compilationUnit));
323 if (metaType != QMetaType::fromType<QString>())
324 resultVariant.convert(metaType);
326 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
327 metaType.destruct(dataPtr);
328 metaType.construct(dataPtr, resultVariant.constData());
334 const QQmlPropertyData *pd,
335 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
336 const QV4::CompiledData::Binding *binding)
338 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
340 [binding](
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
341 return compilationUnit->bindingValueAsString(binding);
344 return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
345 QPropertyBindingSourceLocation());
349 const QMetaType &propertyType,
350 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
351 const QQmlTranslation &translationData)
353 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
356 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
357 Q_UNUSED(compilationUnit);
358 return translationData.translate();
361 return QUntypedPropertyBinding(propertyType, translationBinding,
362 QPropertyBindingSourceLocation());
365QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(
bool *isUndefined)
367 QV4::ExecutionEngine *v4 = engine()->handle();
369 const QV4::Value *argv =
nullptr;
370 const QV4::Value *thisObject =
nullptr;
371 QV4::BoundFunction *b =
nullptr;
372 if ((b = m_boundFunction.as<QV4::BoundFunction>())) {
373 QV4::Heap::MemberData *args = b->boundArgs();
375 argc = args->values.size;
376 argv = args->values.data();
378 thisObject = &b->d()->boundThis;
380 QV4::Scope scope(v4);
381 QV4::JSCallData jsCall(thisObject, argv, argc);
383 return QQmlJavaScriptExpression::evaluate(jsCall.callData(scope), isUndefined);
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)