7#include <private/qqmlbinding_p.h>
8#include <private/qqmlglobal_p.h>
9#include <private/qqmlscriptstring_p.h>
10#include <private/qv4functionobject_p.h>
11#include <private/qv4jscall_p.h>
12#include <private/qv4qmlcontext_p.h>
14#include <QtQml/qqmlinfo.h>
16#include <QtCore/qloggingcategory.h>
20using namespace Qt::Literals::StringLiterals;
24QUntypedPropertyBinding QQmlPropertyBinding::create(
const QQmlPropertyData *pd, QV4::Function *function,
25 QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt,
26 QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
29 return create(pd->propType(), function, obj, ctxt, scope, target, targetIndex);
32QUntypedPropertyBinding QQmlPropertyBinding::create(QMetaType propertyType, QV4::Function *function,
34 const QQmlRefPointer<QQmlContextData> &ctxt,
35 QV4::ExecutionContext *scope, QObject *target,
36 QQmlPropertyIndex targetIndex)
38 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
39 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
40 auto binding =
new (buffer) QQmlPropertyBinding(propertyType, target, targetIndex,
41 TargetData::WithoutBoundFunction);
42 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
43 Q_ASSERT(binding->jsExpression() == js);
44 Q_ASSERT(js->asBinding() == binding);
46 binding->jsExpression()->setNotifyOnValueChanged(
true);
47 binding->jsExpression()->setContext(ctxt);
48 binding->jsExpression()->setScopeObject(obj);
49 binding->jsExpression()->setupFunction(scope, function);
50 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
53QUntypedPropertyBinding QQmlPropertyBinding::createFromCodeString(
const QQmlPropertyData *pd,
const QString& str, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt,
const QString &url, quint16 lineNumber, QObject *target, QQmlPropertyIndex targetIndex)
55 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
56 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
57 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
58 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
59 Q_ASSERT(binding->jsExpression() == js);
60 Q_ASSERT(js->asBinding() == binding);
62 binding->jsExpression()->setNotifyOnValueChanged(
true);
63 binding->jsExpression()->setContext(ctxt);
64 binding->jsExpression()->createQmlBinding(ctxt, obj, str, url, lineNumber);
65 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
68QUntypedPropertyBinding QQmlPropertyBinding::createFromScriptString(
const QQmlPropertyData *property,
const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt, QObject *target, QQmlPropertyIndex targetIndex)
70 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
72 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) {
76 auto scopeObject = obj ? obj : scriptPrivate->scope;
78 QV4::Function *runtimeFunction =
nullptr;
80 QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context);
81 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
82 if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) {
83 url = ctxtdata->urlString();
84 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
85 runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId);
89 return createFromCodeString(property, scriptPrivate->script, obj, ctxtdata, url, scriptPrivate->lineNumber, target, targetIndex);
91 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
92 +
sizeof(QQmlPropertyBindingJS)+jsExpressionOffsetLength()];
93 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(property->propType()), target, targetIndex, TargetData::WithoutBoundFunction);
94 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJS();
95 Q_ASSERT(binding->jsExpression() == js);
96 Q_ASSERT(js->asBinding() == binding);
97 js->setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
99 QV4::ExecutionEngine *v4 = engine->v4Engine.get();
100 QV4::Scope scope(v4);
101 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, scopeObject));
102 js->setupFunction(qmlContext, runtimeFunction);
103 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
106QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(
const QQmlPropertyData *pd, QV4::BoundFunction *function, QObject *obj,
const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope, QObject *target, QQmlPropertyIndex targetIndex)
108 auto buffer =
new std::byte[QQmlPropertyBinding::getSizeEnsuringAlignment()
109 +
sizeof(QQmlPropertyBindingJSForBoundFunction)+jsExpressionOffsetLength()];
110 auto binding =
new(buffer) QQmlPropertyBinding(QMetaType(pd->propType()), target, targetIndex, TargetData::HasBoundFunction);
111 auto js =
new(buffer + QQmlPropertyBinding::getSizeEnsuringAlignment() + jsExpressionOffsetLength()) QQmlPropertyBindingJSForBoundFunction();
112 Q_ASSERT(binding->jsExpression() == js);
113 Q_ASSERT(js->asBinding() == binding);
115 binding->jsExpression()->setNotifyOnValueChanged(
true);
116 binding->jsExpression()->setContext(ctxt);
117 binding->jsExpression()->setScopeObject(obj);
118 binding->jsExpression()->setupFunction(scope, function->function());
119 js->m_boundFunction.set(function->engine(), *function);
120 return QUntypedPropertyBinding(
static_cast<QPropertyBindingPrivate *>(QPropertyBindingPrivatePtr(binding).data()));
124
125
126
127
128
129
130
131
132
135void QQmlPropertyBindingJS::expressionChanged()
137 if (!hasValidContext())
140 auto binding = asBinding();
141 if (!binding->propertyDataPtr)
143 const auto currentTag = m_error.tag();
144 if (currentTag == InEvaluationLoop) {
146 auto location = QQmlJavaScriptExpression::sourceLocation();
147 err.setUrl(QUrl{location.sourceFile});
148 err.setLine(location.line);
149 err.setColumn(location.column);
150 const auto ctxt = context();
151 QQmlEngine *engine = ctxt ? ctxt->engine() :
nullptr;
153 err.setDescription(asBinding()->createBindingLoopErrorDescription());
155 err.setDescription(QString::fromLatin1(
"Binding loop detected"));
156 err.setObject(asBinding()->target());
157 qmlWarning(
this->scopeObject(), err);
160 m_error.setTag(InEvaluationLoop);
161 PendingBindingObserverList bindingObservers;
162 binding->evaluateRecursive(bindingObservers);
163 binding->notifyNonRecursive(bindingObservers);
164 m_error.setTag(NoTag);
167QQmlPropertyBinding::QQmlPropertyBinding(QMetaType mt, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction)
168 : QPropertyBindingPrivate(mt,
169 bindingFunctionVTableForQQmlPropertyBinding(mt),
170 QPropertyBindingSourceLocation(),
true)
172 static_assert (std::is_trivially_destructible_v<TargetData>);
173 static_assert (
sizeof(TargetData) +
sizeof(DeclarativeErrorCallback) <=
sizeof(QPropertyBindingSourceLocation));
174 static_assert (
alignof(TargetData) <=
alignof(QPropertyBindingSourceLocation));
175 const auto state = hasBoundFunction ? TargetData::HasBoundFunction : TargetData::WithoutBoundFunction;
176 new (&declarativeExtraData) TargetData {target, targetIndex, state};
177 errorCallBack = bindingErrorCallback;
184
185
186
187
188
189
191 std::byte *qpropertyPointer =
reinterpret_cast<
std::byte *>(dataPtr);
192 qpropertyPointer += type.sizeOf();
193 constexpr auto alignment =
alignof(QtPrivate::QPropertyBindingData *);
194 auto aligned = (quintptr(qpropertyPointer) + alignment - 1) & ~(alignment - 1);
195 return reinterpret_cast<QtPrivate::QPropertyBindingData *>(aligned);
198void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep,
void *dataPtr)
200 const QQmlPropertyData *propertyData =
nullptr;
201 QQmlPropertyData valueTypeData;
202 QQmlData *data = QQmlData::get(target(),
false);
204 if (Q_UNLIKELY(!data->propertyCache))
205 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
207 propertyData = data->propertyCache->property(targetIndex().coreIndex());
208 Q_ASSERT(propertyData);
209 Q_ASSERT(!targetIndex().hasValueTypeIndex());
210 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
214 auto writeBackCurrentValue = [&](QVariant &¤tValue) {
215 if (currentValue.metaType() != valueMetaType())
216 currentValue.convert(valueMetaType());
217 auto metaType = valueMetaType();
218 metaType.destruct(dataPtr);
219 metaType.construct(dataPtr, currentValue.constData());
221 if (prop.isResettable()) {
225 const auto storage = qGetBindingStorage(target());
226 auto bindingData = storage->bindingData(propertyDataPtr);
228 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
229 QPropertyBindingDataPointer bindingDataPointer{bindingData};
230 auto firstObserver = takeObservers();
231 bindingData->d_ref() = 0;
233 bindingDataPointer.setObservers(firstObserver.ptr);
235 Q_ASSERT(!bindingData->hasBinding());
236 setIsUndefined(
true);
238 auto state = QtPrivate::suspendCurrentBindingStatus();
240 QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
241 QtPrivate::restoreBindingStatus(state);
242 writeBackCurrentValue(std::move(currentValue));
245 bindingData = storage->bindingData(propertyDataPtr);
247 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
248 bindingDataPointer = QPropertyBindingDataPointer {bindingData};
251 if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
252 qCWarning(lcQQPropertyBinding) <<
"Resetting " << prop.name() <<
"due to the binding becoming undefined caused a new binding to be installed\n"
253 <<
"The old binding binding will be abandoned";
258 firstObserver = bindingDataPointer.firstObserver();
259 bindingData->d_ref() =
reinterpret_cast<quintptr>(
this) | QtPrivate::QPropertyBindingData::BindingBit;
261 prependObserver(firstObserver);
264 auto location = jsExpression()->sourceLocation();
265 qmlError.setColumn(location.column);
266 qmlError.setLine(location.line);
267 qmlError.setUrl(QUrl {location.sourceFile});
268 const QString description = QStringLiteral(R"(QML %1: Unable to assign [undefined] to "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
269 qmlError.setDescription(description);
270 qmlError.setObject(target());
271 ep->warning(qmlError);
275QString QQmlPropertyBinding::createBindingLoopErrorDescription()
277 const QQmlPropertyData *propertyData =
nullptr;
278 QQmlPropertyData valueTypeData;
279 QQmlData *data = QQmlData::get(target(),
false);
281 if (Q_UNLIKELY(!data->propertyCache))
282 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
284 propertyData = data->propertyCache->property(targetIndex().coreIndex());
285 Q_ASSERT(propertyData);
286 Q_ASSERT(!targetIndex().hasValueTypeIndex());
287 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
288 return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
291void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
293 auto This =
static_cast<QQmlPropertyBinding *>(that);
294 auto target = This->target();
295 auto engine = qmlEngine(target);
299 auto error = This->bindingError();
301 auto location = This->jsExpression()->sourceLocation();
302 qmlError.setColumn(location.column);
303 qmlError.setLine(location.line);
304 qmlError.setUrl(QUrl {location.sourceFile});
305 auto description = error.description();
306 if (error.type() == QPropertyBindingError::BindingLoop) {
307 description = This->createBindingLoopErrorDescription();
309 qmlError.setDescription(description);
310 qmlError.setObject(target);
311 QQmlEnginePrivate::get(engine)->warning(qmlError);
314template<
typename TranslateWithUnit>
316 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
317 TranslateWithUnit &&translateWithUnit)
319 return [compilationUnit, translateWithUnit](QMetaType metaType,
void *dataPtr) ->
bool {
321 QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
323 QVariant resultVariant(translateWithUnit(compilationUnit));
324 if (metaType != QMetaType::fromType<QString>())
325 resultVariant.convert(metaType);
327 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
328 metaType.destruct(dataPtr);
329 metaType.construct(dataPtr, resultVariant.constData());
335 const QQmlPropertyData *pd,
336 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
337 const QV4::CompiledData::Binding *binding)
339 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
341 [binding](
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
342 return compilationUnit->bindingValueAsString(binding);
345 return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
346 QPropertyBindingSourceLocation());
350 const QMetaType &propertyType,
351 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
352 const QQmlTranslation &translationData)
354 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
357 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
358 Q_UNUSED(compilationUnit);
359 return translationData.translate();
362 return QUntypedPropertyBinding(propertyType, translationBinding,
363 QPropertyBindingSourceLocation());
366QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(
bool *isUndefined)
368 QV4::ExecutionEngine *v4 = engine()->handle();
370 const QV4::Value *argv =
nullptr;
371 const QV4::Value *thisObject =
nullptr;
372 QV4::BoundFunction *b =
nullptr;
373 if ((b = m_boundFunction.as<QV4::BoundFunction>())) {
374 QV4::Heap::MemberData *args = b->boundArgs();
376 argc = args->values.size;
377 argv = args->values.data();
379 thisObject = &b->d()->boundThis;
381 QV4::Scope scope(v4);
382 QV4::JSCallData jsCall(thisObject, argv, argc);
384 return QQmlJavaScriptExpression::evaluate(jsCall.callData(scope), isUndefined);
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QtPrivate::QPropertyBindingData * bindingDataFromPropertyData(QUntypedPropertyData *dataPtr, QMetaType type)
auto qQmlTranslationPropertyBindingCreateBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, TranslateWithUnit &&translateWithUnit)