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 HasBoundFunction::No);
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 makeUntyped(binding);
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, HasBoundFunction::No);
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 makeUntyped(binding);
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, HasBoundFunction::No);
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 makeUntyped(binding);
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, HasBoundFunction::Yes);
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 makeUntyped(binding);
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,
168 QQmlPropertyIndex targetIndex,
169 HasBoundFunction hasBoundFunction)
170 : QQmlPropertyBindingBase(target, targetIndex, mt,
171 bindingFunctionVTableForQQmlPropertyBinding(mt),
172 BindingKind::JavaScript, hasBoundFunction)
174 errorCallBack = bindingErrorCallback;
181
182
183
184
185
186
188 std::byte *qpropertyPointer =
reinterpret_cast<
std::byte *>(dataPtr);
189 qpropertyPointer += type.sizeOf();
190 constexpr auto alignment =
alignof(QtPrivate::QPropertyBindingData *);
191 auto aligned = (quintptr(qpropertyPointer) + alignment - 1) & ~(alignment - 1);
192 return reinterpret_cast<QtPrivate::QPropertyBindingData *>(aligned);
195void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep,
void *dataPtr)
197 const QQmlPropertyData *propertyData =
nullptr;
198 QQmlPropertyData valueTypeData;
199 QQmlData *data = QQmlData::get(target(),
false);
201 if (Q_UNLIKELY(!data->propertyCache))
202 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
204 propertyData = data->propertyCache->property(targetIndex().coreIndex());
205 Q_ASSERT(propertyData);
206 Q_ASSERT(!targetIndex().hasValueTypeIndex());
207 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
211 auto writeBackCurrentValue = [&](QVariant &¤tValue) {
212 if (currentValue.metaType() != valueMetaType())
213 currentValue.convert(valueMetaType());
214 auto metaType = valueMetaType();
215 metaType.destruct(dataPtr);
216 metaType.construct(dataPtr, currentValue.constData());
218 if (prop.isResettable()) {
222 const auto storage = qGetBindingStorage(target());
223 auto bindingData = storage->bindingData(propertyDataPtr);
225 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
226 QPropertyBindingDataPointer bindingDataPointer{bindingData};
227 auto firstObserver = takeObservers();
228 bindingData->d_ref() = 0;
230 bindingDataPointer.setObservers(firstObserver.ptr);
232 Q_ASSERT(!bindingData->hasBinding());
233 setIsUndefined(
true);
235 auto state = QtPrivate::suspendCurrentBindingStatus();
237 QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
238 QtPrivate::restoreBindingStatus(state);
239 writeBackCurrentValue(std::move(currentValue));
242 bindingData = storage->bindingData(propertyDataPtr);
244 bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
245 bindingDataPointer = QPropertyBindingDataPointer {bindingData};
248 if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
249 qCWarning(lcQQPropertyBinding) <<
"Resetting " << prop.name() <<
"due to the binding becoming undefined caused a new binding to be installed\n"
250 <<
"The old binding binding will be abandoned";
255 firstObserver = bindingDataPointer.firstObserver();
256 bindingData->d_ref() =
reinterpret_cast<quintptr>(
this) | QtPrivate::QPropertyBindingData::BindingBit;
258 prependObserver(firstObserver);
261 auto location = jsExpression()->sourceLocation();
262 qmlError.setColumn(location.column);
263 qmlError.setLine(location.line);
264 qmlError.setUrl(QUrl {location.sourceFile});
265 const QString description = QStringLiteral(R"(QML %1: Unable to assign [undefined] to "%2")").arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
266 qmlError.setDescription(description);
267 qmlError.setObject(target());
268 ep->warning(qmlError);
272QString QQmlPropertyBinding::createBindingLoopErrorDescription()
274 const QQmlPropertyData *propertyData =
nullptr;
275 QQmlPropertyData valueTypeData;
276 QQmlData *data = QQmlData::get(target(),
false);
278 if (Q_UNLIKELY(!data->propertyCache))
279 data->propertyCache = QQmlMetaType::propertyCache(target()->metaObject());
281 propertyData = data->propertyCache->property(targetIndex().coreIndex());
282 Q_ASSERT(propertyData);
283 Q_ASSERT(!targetIndex().hasValueTypeIndex());
284 QQmlProperty prop = QQmlPropertyPrivate::restore(target(), *propertyData, &valueTypeData,
nullptr);
285 return R"(QML %1: Binding loop detected for property "%2")"_L1.arg(QQmlMetaType::prettyTypeName(target()) , prop.name());
288void QQmlPropertyBinding::bindingErrorCallback(QPropertyBindingPrivate *that)
290 auto This =
static_cast<QQmlPropertyBinding *>(that);
291 auto target = This->target();
292 auto engine = qmlEngine(target);
296 auto error = This->bindingError();
298 auto location = This->jsExpression()->sourceLocation();
299 qmlError.setColumn(location.column);
300 qmlError.setLine(location.line);
301 qmlError.setUrl(QUrl {location.sourceFile});
302 auto description = error.description();
303 if (error.type() == QPropertyBindingError::BindingLoop) {
304 description = This->createBindingLoopErrorDescription();
306 qmlError.setDescription(description);
307 qmlError.setObject(target);
308 QQmlEnginePrivate::get(engine)->warning(qmlError);
311template<
typename TranslateWithUnit>
313 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
314 TranslateWithUnit &&translateWithUnit)
316 return [compilationUnit, translateWithUnit](QMetaType metaType,
void *dataPtr) ->
bool {
318 QQmlEnginePrivate::get(compilationUnit->engine)->translationLanguage.value();
320 QVariant resultVariant(translateWithUnit(compilationUnit));
321 if (metaType != QMetaType::fromType<QString>())
322 resultVariant.convert(metaType);
324 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
325 metaType.destruct(dataPtr);
326 metaType.construct(dataPtr, resultVariant.constData());
332 const QQmlPropertyData *pd,
333 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
334 const QV4::CompiledData::Binding *binding)
336 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
338 [binding](
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
339 return compilationUnit->bindingValueAsString(binding);
342 return QUntypedPropertyBinding(QMetaType(pd->propType()), translationBinding,
343 QPropertyBindingSourceLocation());
347 const QMetaType &propertyType,
348 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
349 const QQmlTranslation &translationData)
351 auto translationBinding = qQmlTranslationPropertyBindingCreateBinding(
354 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) {
355 Q_UNUSED(compilationUnit);
356 return translationData.translate();
359 return QUntypedPropertyBinding(propertyType, translationBinding,
360 QPropertyBindingSourceLocation());
363QV4::ReturnedValue QQmlPropertyBindingJSForBoundFunction::evaluate(
bool *isUndefined)
365 QV4::ExecutionEngine *v4 = engine()->handle();
367 const QV4::Value *argv =
nullptr;
368 const QV4::Value *thisObject =
nullptr;
369 QV4::BoundFunction *b =
nullptr;
370 if ((b = m_boundFunction.as<QV4::BoundFunction>())) {
371 QV4::Heap::MemberData *args = b->boundArgs();
373 argc = args->values.size;
374 argv = args->values.data();
376 thisObject = &b->d()->boundThis;
378 QV4::Scope scope(v4);
379 QV4::JSCallData jsCall(thisObject, argv, argc);
381 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)