Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmlpropertyvalidator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <private/qqmlcustomparser_p.h>
7#include <private/qqmlglobal_p.h>
8#include <private/qqmlirbuilder_p.h>
9#include <private/qqmlpropertycachecreator_p.h>
10#include <private/qqmlpropertyresolver_p.h>
11#include <private/qqmlstringconverters_p.h>
12#include <private/qqmlsignalnames_p.h>
13
14#include <QtCore/qdatetime.h>
15
17
18static bool isPrimitiveType(QMetaType metaType)
19{
20 switch (metaType.id()) {
21#define HANDLE_PRIMITIVE(Type, id, T) \
22 case QMetaType::Type:
24#undef HANDLE_PRIMITIVE
25 return true;
26 default:
27 return false;
28 }
29}
30
32 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
33 : enginePrivate(enginePrivate)
34 , compilationUnit(compilationUnit)
35 , imports(imports)
36 , qmlUnit(compilationUnit->unitData())
37 , propertyCaches(compilationUnit->propertyCaches)
38 , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
39{
40 bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
41}
42
44{
45 return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr);
46}
47
48typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
49
51{
53 {
54 return name < binding->propertyNameIndex;
55 }
57 {
58 return binding->propertyNameIndex < name;
59 }
61 {
62 return lhs->propertyNameIndex < rhs->propertyNameIndex;
63 }
64};
65
66QVector<QQmlError> QQmlPropertyValidator::validateObject(
67 int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
68{
69 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
70 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
71 const auto errors = validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr);
72 if (!errors.isEmpty())
73 return errors;
74 }
75
78 Q_ASSERT(obj->nBindings == 1);
79 const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
80 Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object);
81 return validateObject(componentBinding->value.objectIndex, componentBinding);
82 }
83
84 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches.at(objectIndex);
85 if (!propertyCache)
86 return QVector<QQmlError>();
87
88 QQmlCustomParser *customParser = nullptr;
89 if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
90
91 // This binding instantiates a separate object. The separate object can have an ID and its
92 // own group properties even if it's then assigned to a value type, for example a 'var', or
93 // anything with an invokable ctor taking a QObject*.
94 populatingValueTypeGroupProperty = false;
95
96 const auto type = typeRef->type();
97 if (type.isValid())
98 customParser = type.customParser();
99 }
100
101 QList<const QV4::CompiledData::Binding*> customBindings;
102
103 // Collect group properties first for sanity checking
104 // vector values are sorted by property name string index.
105 GroupPropertyVector groupProperties;
106 const QV4::CompiledData::Binding *binding = obj->bindingTable();
107 for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
108 if (!binding->isGroupProperty())
109 continue;
110
112 continue;
113
114 if (populatingValueTypeGroupProperty) {
115 return recordError(binding->location, tr("Property assignment expected"));
116 }
117
118 GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
119 groupProperties.insert(pos, binding);
120 }
121
122 QQmlPropertyResolver propertyResolver(propertyCache);
123
124 QString defaultPropertyName;
125 const QQmlPropertyData *defaultProperty = nullptr;
126 if (obj->indexOfDefaultPropertyOrAlias != -1) {
127 const QQmlPropertyCache *cache = propertyCache->parent().data();
128 defaultPropertyName = cache->defaultPropertyName();
129 defaultProperty = cache->defaultProperty();
130 } else {
131 defaultPropertyName = propertyCache->defaultPropertyName();
132 defaultProperty = propertyCache->defaultProperty();
133 }
134
135 QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
136
137 binding = obj->bindingTable();
138 for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
139 QString name = stringAt(binding->propertyNameIndex);
140 const QV4::CompiledData::Binding::Type bindingType = binding->type();
141 const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
142
143 if (customParser) {
146 customBindings << binding;
147 continue;
148 }
150 && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
151 customBindings << binding;
152 continue;
153 }
154 }
155
156 bool bindingToDefaultProperty = false;
157 bool isGroupProperty = instantiatingBinding
158 && instantiatingBinding->type() == QV4::CompiledData::Binding::Type_GroupProperty;
159
160 bool notInRevision = false;
161 const QQmlPropertyData *pd = nullptr;
162 if (!name.isEmpty()) {
165 pd = propertyResolver.signal(name, &notInRevision);
166 } else {
167 pd = propertyResolver.property(name, &notInRevision,
169 }
170
171 if (notInRevision) {
172 QString typeName = stringAt(obj->inheritedTypeNameIndex);
173 if (auto *objectType = resolvedType(obj->inheritedTypeNameIndex)) {
174 const auto type = objectType->type();
175 if (type.isValid()) {
176 const auto version = objectType->version();
177 return recordError(binding->location,
178 tr("\"%1.%2\" is not available in %3 %4.%5.")
179 .arg(typeName).arg(name).arg(type.module())
180 .arg(version.majorVersion())
181 .arg(version.minorVersion()));
182 }
183 } else {
184 return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
185 }
186 }
187 } else {
188 if (isGroupProperty)
189 return recordError(binding->location, tr("Cannot assign a value directly to a grouped property"));
190
191 pd = defaultProperty;
192 name = defaultPropertyName;
193 bindingToDefaultProperty = true;
194 }
195
196 if (pd)
197 collectedBindingPropertyData[i] = pd;
198
199 if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
201 QQmlImportNamespace *typeNamespace = nullptr;
202 imports->resolveType(
203 QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
204 &type, nullptr, &typeNamespace);
205 if (typeNamespace)
206 return recordError(binding->location, tr("Invalid use of namespace"));
207 return recordError(binding->location, tr("Invalid attached object assignment"));
208 }
209
211 && (pd || binding->isAttachedProperty() || binding->isGroupProperty())) {
212 const bool populatingValueTypeGroupProperty
213 = pd
216 const QVector<QQmlError> subObjectValidatorErrors
217 = validateObject(binding->value.objectIndex, binding,
218 populatingValueTypeGroupProperty);
219 if (!subObjectValidatorErrors.isEmpty())
220 return subObjectValidatorErrors;
221 }
222
223 // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
227 continue;
228 }
229
231 || (!pd && bindingType == QV4::CompiledData::Binding::Type_GroupProperty)) {
232 if (instantiatingBinding && (instantiatingBinding->isAttachedProperty()
233 || instantiatingBinding->isGroupProperty())) {
234 return recordError(
235 binding->location, tr("%1 properties cannot be used here")
237 ? QStringLiteral("Attached")
238 : QStringLiteral("Group")));
239 }
240 continue;
241 } else if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
242 continue;
243 }
244
245 if (pd) {
246 GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
247 const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
248
249 if (!pd->isWritable()
250 && !pd->isQList()
251 && !binding->isGroupProperty()
253 ) {
254
255 if (assigningToGroupProperty && bindingType < QV4::CompiledData::Binding::Type_Object)
256 return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
257 return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
258 }
259
260 if (!pd->isQList() && (bindingFlags & QV4::CompiledData::Binding::IsListItem)) {
262 if (pd->propType() == QMetaType::fromType<QQmlScriptString>())
263 error = tr( "Cannot assign multiple values to a script property");
264 else
265 error = tr( "Cannot assign multiple values to a singular property");
266 return recordError(binding->valueLocation, error);
267 }
268
269 if (!bindingToDefaultProperty
270 && !binding->isGroupProperty()
272 && assigningToGroupProperty) {
274 if (loc < (*assignedGroupProperty)->valueLocation)
275 loc = (*assignedGroupProperty)->valueLocation;
276
277 if (pd && QQmlMetaType::isValueType(pd->propType()))
278 return recordError(loc, tr("Property has already been assigned a value"));
279 return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
280 }
281
282 if (bindingType < QV4::CompiledData::Binding::Type_Script) {
283 QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding);
284 if (bindingError.isValid())
285 return recordError(bindingError);
286 } else if (bindingType == QV4::CompiledData::Binding::Type_Object) {
287 QQmlError bindingError = validateObjectBinding(pd, name, binding);
288 if (bindingError.isValid())
289 return recordError(bindingError);
290 } else if (binding->isGroupProperty()) {
293 if (!pd->isWritable()) {
294 return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
295 }
296 } else {
297 return recordError(binding->location, tr("Invalid grouped property access"));
298 }
299 } else {
300 const QMetaType type = pd->propType();
301 if (isPrimitiveType(type)) {
302 return recordError(
303 binding->location,
304 tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".")
305 .arg(name, QString::fromUtf8(type.name()))
306 );
307 }
308
310 auto mo = type.metaObject();
311 if (!mo) {
312 return recordError(binding->location,
313 tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is neither a value nor an object type")
314 .arg(name, QString::fromUtf8(type.name()))
315 );
316 }
318 return recordError(binding->location,
319 tr("Unsupported grouped property access: Property \"%1\" with type \"%2\" has a dynamic meta-object.")
320 .arg(name, QString::fromUtf8(type.name()))
321 );
322 }
323 // fall through, this is okay
324 }
325 }
326 }
327 } else {
328 if (customParser) {
329 customBindings << binding;
330 continue;
331 }
332 if (bindingToDefaultProperty) {
333 return recordError(binding->location, tr("Cannot assign to non-existent default property"));
334 } else {
335 return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name));
336 }
337 }
338 }
339
340 if (obj->idNameIndex) {
341 if (populatingValueTypeGroupProperty)
342 return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type"));
343
344 bool notInRevision = false;
345 collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
346 }
347
348 if (customParser && !customBindings.isEmpty()) {
349 customParser->clearErrors();
350 customParser->validator = this;
351 customParser->engine = enginePrivate;
352 customParser->imports = imports;
353 customParser->verifyBindings(
354 enginePrivate->v4engine()->executableCompilationUnit(
355 QQmlRefPointer<QV4::CompiledData::CompilationUnit>(compilationUnit)),
356 customBindings);
357 customParser->validator = nullptr;
358 customParser->engine = nullptr;
359 customParser->imports = (QQmlImports*)nullptr;
360 QVector<QQmlError> parserErrors = customParser->errors();
361 if (!parserErrors.isEmpty())
362 return parserErrors;
363 }
364
365 (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
366
367 QVector<QQmlError> noError;
368 return noError;
369}
370
371QQmlError QQmlPropertyValidator::validateLiteralBinding(
372 const QQmlPropertyCache::ConstPtr &propertyCache, const QQmlPropertyData *property,
373 const QV4::CompiledData::Binding *binding) const
374{
375 if (property->isQList()) {
376 return qQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
377 }
378
379 QQmlError noError;
380
381 if (property->isEnum()) {
383 return noError;
384
385 // TODO: For historical reasons you can assign any number to an enum property alias
386 // This can be fixed with an opt-out mechanism, for example a pragma.
387 if (property->isAlias() && binding->isNumberBinding())
388 return noError;
389
390 QString value = compilationUnit->bindingValueAsString(binding);
391 QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
392 bool ok;
393 if (p.isFlagType()) {
394 p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
395 } else
396 p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
397
398 if (!ok) {
399 return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
400 }
401 return noError;
402 }
403
404 auto warnOrError = [&](const QString &error) {
405 return qQmlCompileError(binding->valueLocation, error);
406 };
407
408 const QV4::CompiledData::Binding::Type bindingType = binding->type();
409 const auto isStringBinding = [&]() -> bool {
410 // validateLiteralBinding is not supposed to be used on scripts
412 return bindingType == QV4::CompiledData::Binding::Type_String;
413 };
414
415 switch (property->propType().id()) {
417 break;
418 case QMetaType::QString: {
419 if (!binding->evaluatesToString()) {
420 return warnOrError(tr("Invalid property assignment: string expected"));
421 }
422 }
423 break;
424 case QMetaType::QStringList: {
425 if (!binding->evaluatesToString()) {
426 return warnOrError(tr("Invalid property assignment: string or string list expected"));
427 }
428 }
429 break;
430 case QMetaType::QByteArray: {
432 return warnOrError(tr("Invalid property assignment: byte array expected"));
433 }
434 break;
435 case QMetaType::QUrl: {
437 return warnOrError(tr("Invalid property assignment: url expected"));
438 }
439 break;
440 case QMetaType::UInt: {
441 if (bindingType == QV4::CompiledData::Binding::Type_Number) {
442 double d = compilationUnit->bindingValueAsNumber(binding);
443 if (double(uint(d)) == d)
444 return noError;
445 }
446 return warnOrError(tr("Invalid property assignment: unsigned int expected"));
447 }
448 break;
449 case QMetaType::Int: {
450 if (bindingType == QV4::CompiledData::Binding::Type_Number) {
451 double d = compilationUnit->bindingValueAsNumber(binding);
452 if (double(int(d)) == d)
453 return noError;
454 }
455 return warnOrError(tr("Invalid property assignment: int expected"));
456 }
457 break;
458 case QMetaType::Float: {
459 if (bindingType != QV4::CompiledData::Binding::Type_Number) {
460 return warnOrError(tr("Invalid property assignment: number expected"));
461 }
462 }
463 break;
464 case QMetaType::Double: {
465 if (bindingType != QV4::CompiledData::Binding::Type_Number) {
466 return warnOrError(tr("Invalid property assignment: number expected"));
467 }
468 }
469 break;
470 case QMetaType::QColor: {
471 bool ok = false;
472 if (isStringBinding())
474 if (!ok) {
475 return warnOrError(tr("Invalid property assignment: color expected"));
476 }
477 }
478 break;
479#if QT_CONFIG(datestring)
480 case QMetaType::QDate: {
481 bool ok = false;
482 if (isStringBinding())
483 QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
484 if (!ok) {
485 return warnOrError(tr("Invalid property assignment: date expected"));
486 }
487 }
488 break;
489 case QMetaType::QTime: {
490 bool ok = false;
491 if (isStringBinding())
492 QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
493 if (!ok) {
494 return warnOrError(tr("Invalid property assignment: time expected"));
495 }
496 }
497 break;
498 case QMetaType::QDateTime: {
499 bool ok = false;
500 if (isStringBinding())
501 QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
502 if (!ok) {
503 return warnOrError(tr("Invalid property assignment: datetime expected"));
504 }
505 }
506 break;
507#endif // datestring
508 case QMetaType::QPoint: {
509 bool ok = false;
510 if (isStringBinding())
512 if (!ok) {
513 return warnOrError(tr("Invalid property assignment: point expected"));
514 }
515 }
516 break;
517 case QMetaType::QPointF: {
518 bool ok = false;
519 if (isStringBinding())
521 if (!ok) {
522 return warnOrError(tr("Invalid property assignment: point expected"));
523 }
524 }
525 break;
526 case QMetaType::QSize: {
527 bool ok = false;
528 if (isStringBinding())
530 if (!ok) {
531 return warnOrError(tr("Invalid property assignment: size expected"));
532 }
533 }
534 break;
535 case QMetaType::QSizeF: {
536 bool ok = false;
537 if (isStringBinding())
539 if (!ok) {
540 return warnOrError(tr("Invalid property assignment: size expected"));
541 }
542 }
543 break;
544 case QMetaType::QRect: {
545 bool ok = false;
546 if (isStringBinding())
548 if (!ok) {
549 return warnOrError(tr("Invalid property assignment: rect expected"));
550 }
551 }
552 break;
553 case QMetaType::QRectF: {
554 bool ok = false;
555 if (isStringBinding())
557 if (!ok) {
558 return warnOrError(tr("Invalid property assignment: point expected"));
559 }
560 }
561 break;
562 case QMetaType::Bool: {
563 if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
564 return warnOrError(tr("Invalid property assignment: boolean expected"));
565 }
566 }
567 break;
568 case QMetaType::QVector2D:
569 case QMetaType::QVector3D:
570 case QMetaType::QVector4D:
571 case QMetaType::QQuaternion: {
572 auto typeName = [&]() {
573 switch (property->propType().id()) {
574 case QMetaType::QVector2D: return QStringLiteral("2D vector");
575 case QMetaType::QVector3D: return QStringLiteral("3D vector");
576 case QMetaType::QVector4D: return QStringLiteral("4D vector");
577 case QMetaType::QQuaternion: return QStringLiteral("quaternion");
578 default: return QString();
579 }
580 };
582 compilationUnit->bindingValueAsString(binding),
583 property->propType());
584 if (!result.isValid()) {
585 return warnOrError(tr("Invalid property assignment: %1 expected")
586 .arg(typeName()));
587 }
588 }
589 break;
590 case QMetaType::QRegularExpression:
591 return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
592 default: {
593 // generate single literal value assignment to a list property if required
594 if (property->propType() == QMetaType::fromType<QList<qreal> >()) {
595 if (bindingType != QV4::CompiledData::Binding::Type_Number) {
596 return warnOrError(tr("Invalid property assignment: number or array of numbers expected"));
597 }
598 break;
599 } else if (property->propType() == QMetaType::fromType<QList<int> >()) {
600 bool ok = (bindingType == QV4::CompiledData::Binding::Type_Number);
601 if (ok) {
602 double n = compilationUnit->bindingValueAsNumber(binding);
603 if (double(int(n)) != n)
604 ok = false;
605 }
606 if (!ok)
607 return warnOrError(tr("Invalid property assignment: int or array of ints expected"));
608 break;
609 } else if (property->propType() == QMetaType::fromType<QList<bool> >()) {
610 if (bindingType != QV4::CompiledData::Binding::Type_Boolean) {
611 return warnOrError(tr("Invalid property assignment: bool or array of bools expected"));
612 }
613 break;
614 } else if (property->propType() == QMetaType::fromType<QList<QUrl> >()) {
615 if (bindingType != QV4::CompiledData::Binding::Type_String) {
616 return warnOrError(tr("Invalid property assignment: url or array of urls expected"));
617 }
618 break;
619 } else if (property->propType() == QMetaType::fromType<QList<QString> >()) {
620 if (!binding->evaluatesToString()) {
621 return warnOrError(tr("Invalid property assignment: string or array of strings expected"));
622 }
623 break;
624 } else if (property->propType() == QMetaType::fromType<QJSValue>()) {
625 break;
626 } else if (property->propType() == QMetaType::fromType<QQmlScriptString>()) {
627 break;
628 } else if (property->isQObject()
629 && bindingType == QV4::CompiledData::Binding::Type_Null) {
630 break;
631 } else if (QQmlMetaType::qmlType(property->propType()).canConstructValueType()) {
632 break;
633 }
634
635 return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QLatin1StringView(property->propType().name())));
636 }
637 break;
638 }
639 return noError;
640}
641
646bool QQmlPropertyValidator::canCoerce(QMetaType to, QQmlPropertyCache::ConstPtr fromMo) const
647{
649
650 if (toMo.isNull()) {
651 // if we have an inline component from the current file,
652 // it is not properly registered at this point, as registration
653 // only occurs after the whole file has been validated
654 // Therefore we need to check the ICs here
655 for (const auto& icDatum : compilationUnit->inlineComponentData) {
656 if (icDatum.qmlType.typeId() == to) {
657 toMo = compilationUnit->propertyCaches.at(icDatum.objectIndex);
658 break;
659 }
660 }
661 }
662
663 while (fromMo) {
664 if (fromMo == toMo)
665 return true;
666 fromMo = fromMo->parent();
667 }
668 return false;
669}
670
671QVector<QQmlError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
672{
673 QVector<QQmlError> errors;
674 errors.append(qQmlCompileError(location, description));
675 return errors;
676}
677
678QVector<QQmlError> QQmlPropertyValidator::recordError(const QQmlError &error) const
679{
680 QVector<QQmlError> errors;
681 errors.append(error);
682 return errors;
683}
684
685QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
686{
687 QQmlError noError;
688
691
692 bool isValueSource = false;
693 bool isPropertyInterceptor = false;
694
695 const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
696 if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) {
697 QQmlPropertyCache::ConstPtr cache = typeRef->createPropertyCache();
698 const QMetaObject *mo = cache ? cache->firstCppMetaObject() : nullptr;
699 QQmlType qmlType;
700 while (mo && !qmlType.isValid()) {
701 qmlType = QQmlMetaType::qmlType(mo);
702 mo = mo->superClass();
703 }
704
705 isValueSource = qmlType.propertyValueSourceCast() != -1;
706 isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1;
707 }
708
709 if (!isValueSource && !isPropertyInterceptor) {
710 return qQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
711 }
712
713 return noError;
714 }
715
716 const QMetaType propType = property->propType();
717 const auto rhsType = [&]() {
718 return stringAt(compilationUnit->objectAt(binding->value.objectIndex)
720 };
721
722 if (QQmlMetaType::isInterface(propType)) {
723 // Can only check at instantiation time if the created sub-object successfully casts to the
724 // target interface.
725 return noError;
726 } else if (propType == QMetaType::fromType<QVariant>()
727 || propType == QMetaType::fromType<QJSValue>()) {
728 // We can convert everything to QVariant :)
729 return noError;
730 } else if (property->isQList()) {
731 const QMetaType listType = QQmlMetaType::listValueType(property->propType());
732 if (!QQmlMetaType::isInterface(listType)) {
733 QQmlPropertyCache::ConstPtr source = propertyCaches.at(binding->value.objectIndex);
734 if (!canCoerce(listType, source)) {
735 return qQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
736 }
737 }
738 return noError;
740 && property->isFunction()) {
741 return noError;
742 } else if (isPrimitiveType(propType)) {
743 auto typeName = QString::fromUtf8(QMetaType(propType).name());
744 return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting \"%3\"")
745 .arg(rhsType())
746 .arg(propertyName)
747 .arg(typeName));
748 } else if (propType == QMetaType::fromType<QQmlScriptString>()) {
749 return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
750 } else if (QQmlMetaType::isValueType(property->propType())) {
751 return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting an object")
752 .arg(rhsType()).arg(propertyName));
753 } else {
754 // We want to use the raw metaObject here as the raw metaobject is the
755 // actual property type before we applied any extensions that might
756 // effect the properties on the type, but don't effect assignability
757 // Not passing a version ensures that we get the raw metaObject.
758 QQmlPropertyCache::ConstPtr propertyMetaObject
760 if (!propertyMetaObject) {
761 // if we have an inline component from the current file,
762 // it is not properly registered at this point, as registration
763 // only occurs after the whole file has been validated
764 // Therefore we need to check the ICs here
765 for (const auto& icDatum: compilationUnit->inlineComponentData) {
766 if (icDatum.qmlType.typeId() == property->propType()) {
767 propertyMetaObject
768 = compilationUnit->propertyCaches.at(icDatum.objectIndex);
769 break;
770 }
771 }
772 }
773
774 if (propertyMetaObject) {
775 // Will be true if the assigned type inherits propertyMetaObject
776 // Determine isAssignable value
777 bool isAssignable = false;
778 QQmlPropertyCache::ConstPtr c = propertyCaches.at(binding->value.objectIndex);
779 while (c && !isAssignable) {
780 isAssignable |= c == propertyMetaObject;
781 c = c->parent();
782 }
783
784 if (!isAssignable) {
785 return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
786 .arg(rhsType()).arg(QLatin1String(property->propType().name())));
787 }
788 } else {
789 return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".")
790 .arg(QLatin1String(property->propType().name())));
791 }
792
793 }
794 return noError;
795}
796
\inmodule QtCore
\inmodule QtCore
Definition qmetatype.h:341
static constexpr QMetaType fromType()
Definition qmetatype.h:2642
int id(int=0) const
Definition qmetatype.h:475
friend class QVariant
Definition qmetatype.h:796
The QQmlCustomParser class allows you to add new arbitrary types to QML.
QVector< QQmlError > errors() const
virtual void verifyBindings(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &, const QList< const QV4::CompiledData::Binding * > &)=0
QV4::ExecutionEngine * v4engine() const
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
bool isValid() const
Returns true if this error is valid, otherwise false.
The QQmlImports class encapsulates one QML document's import statements.
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDetected=nullptr) const
static QMetaType listValueType(QMetaType type)
static QQmlPropertyCache::ConstPtr rawPropertyCacheForType(QMetaType metaType)
static const QMetaObject * metaObjectForValueType(QMetaType type)
static bool isInterface(QMetaType type)
See qmlRegisterInterface() for information about when this will return true.
static QQmlType qmlType(const QString &qualifiedName, QTypeRevision version)
Returns the type (if any) of URI-qualified named qualifiedName and version specified by version_major...
static bool isValueType(QMetaType type)
static QQmlPropertyCache::ConstPtr propertyCacheForType(QMetaType metaType)
QQmlPropertyCache::ConstPtr at(int index) const
QMetaType propType() const
QVector< QQmlError > validate()
QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit)
T * data() const
static bool isHandlerName(QStringView signalName)
static QQmlTypeLoader * get(Engine *engine)
int propertyValueSourceCast() const
Definition qqmltype.cpp:730
int propertyValueInterceptorCast() const
Definition qqmltype.cpp:737
bool isValid() const
Definition qqmltype_p.h:54
static QVariant createValueType(const QJSValue &, QMetaType)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtCore
Definition qvariant.h:65
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
auto mo
[7]
Q_QML_EXPORT QPointF pointFFromString(const QString &, bool *ok=nullptr)
Q_QML_EXPORT unsigned rgbaFromString(const QString &, bool *ok=nullptr)
Q_QML_EXPORT QSizeF sizeFFromString(const QString &, bool *ok=nullptr)
Q_QML_EXPORT QRectF rectFFromString(const QString &, bool *ok=nullptr)
Combined button and popup list for selecting options.
QVector< const QQmlPropertyData * > BindingPropertyData
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ DynamicMetaObject
const char * typeName
#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)
Definition qmetatype.h:67
GLint location
GLenum type
GLbitfield flags
GLuint name
GLfloat n
GLsizei GLsizei GLchar * source
GLhandleARB obj
[2]
const GLubyte * c
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
QT_BEGIN_NAMESPACE QQmlError qQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
QVarLengthArray< const QV4::CompiledData::Binding *, 8 > GroupPropertyVector
static QT_BEGIN_NAMESPACE bool isPrimitiveType(QMetaType metaType)
#define HANDLE_PRIMITIVE(Type, id, T)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define tr(X)
unsigned int quint32
Definition qtypes.h:50
unsigned int uint
Definition qtypes.h:34
const char property[13]
Definition qwizard.cpp:101
bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
static const QMetaObjectPrivate * get(const QMetaObject *metaobject)
\inmodule QtCore
union QV4::CompiledData::Binding::@540 value
bool hasFlag(Flag flag) const
double bindingValueAsNumber(const CompiledData::Binding *binding) const
const CompiledObject * objectAt(int index) const
QString bindingValueAsString(const CompiledData::Binding *binding) const
QQmlRefPointer< ExecutableCompilationUnit > executableCompilationUnit(QQmlRefPointer< QV4::CompiledData::CompilationUnit > &&unit)