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 | |
4 | #include "qqmlvaluetypewrapper_p.h" |
5 | |
6 | #include <private/qqmlvaluetype_p.h> |
7 | #include <private/qqmlbinding_p.h> |
8 | #include <private/qqmlglobal_p.h> |
9 | #include <private/qqmlbuiltinfunctions_p.h> |
10 | |
11 | #include <private/qv4engine_p.h> |
12 | #include <private/qv4functionobject_p.h> |
13 | #include <private/qv4variantobject_p.h> |
14 | #include <private/qv4alloca_p.h> |
15 | #include <private/qv4stackframe_p.h> |
16 | #include <private/qv4objectiterator_p.h> |
17 | #include <private/qv4qobjectwrapper_p.h> |
18 | #include <private/qv4identifiertable_p.h> |
19 | #include <private/qv4lookup_p.h> |
20 | #include <private/qv4sequenceobject_p.h> |
21 | #include <private/qv4arraybuffer_p.h> |
22 | #include <private/qv4dateobject_p.h> |
23 | #include <private/qv4jsonobject_p.h> |
24 | #if QT_CONFIG(regularexpression) |
25 | #include <private/qv4regexpobject_p.h> |
26 | #endif |
27 | #if QT_CONFIG(qml_locale) |
28 | #include <private/qqmllocale_p.h> |
29 | #endif |
30 | #include <QtCore/qloggingcategory.h> |
31 | #include <QtCore/qdatetime.h> |
32 | #include <QtCore/QLine> |
33 | #include <QtCore/QLineF> |
34 | #include <QtCore/QSize> |
35 | #include <QtCore/QSizeF> |
36 | #include <QtCore/QTimeZone> |
37 | |
38 | QT_BEGIN_NAMESPACE |
39 | |
40 | Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) |
41 | |
42 | DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); |
43 | |
44 | namespace QV4 { |
45 | |
46 | Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const |
47 | { |
48 | return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>( |
49 | args: m_gadgetPtr, args: QMetaType(m_metaType), args: m_metaObject, args: nullptr, args: -1, args: NoFlag); |
50 | } |
51 | |
52 | void Heap::QQmlValueTypeWrapper::destroy() |
53 | { |
54 | if (m_gadgetPtr) { |
55 | metaType().destruct(data: m_gadgetPtr); |
56 | ::operator delete(m_gadgetPtr); |
57 | } |
58 | ReferenceObject::destroy(); |
59 | } |
60 | |
61 | void Heap::QQmlValueTypeWrapper::setData(const void *data) |
62 | { |
63 | if (auto *gadget = gadgetPtr()) |
64 | metaType().destruct(data: gadget); |
65 | if (!gadgetPtr()) |
66 | setGadgetPtr(::operator new(metaType().sizeOf())); |
67 | metaType().construct(where: gadgetPtr(), copy: data); |
68 | } |
69 | |
70 | QVariant Heap::QQmlValueTypeWrapper::toVariant() const |
71 | { |
72 | Q_ASSERT(gadgetPtr()); |
73 | return QVariant(metaType(), gadgetPtr()); |
74 | } |
75 | |
76 | bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant) |
77 | { |
78 | Q_ASSERT(isVariant()); |
79 | |
80 | const QMetaType variantReferenceType = variant.metaType(); |
81 | if (variantReferenceType != metaType()) { |
82 | // This is a stale VariantReference. That is, the variant has been |
83 | // overwritten with a different type in the meantime. |
84 | // We need to modify this reference to the updated value type, if |
85 | // possible, or return false if it is not a value type. |
86 | if (QQmlMetaType::isValueType(type: variantReferenceType)) { |
87 | const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type: variantReferenceType); |
88 | if (gadgetPtr()) { |
89 | metaType().destruct(data: gadgetPtr()); |
90 | ::operator delete(gadgetPtr()); |
91 | } |
92 | setGadgetPtr(nullptr); |
93 | setMetaObject(mo); |
94 | setMetaType(variantReferenceType); |
95 | if (!mo) |
96 | return false; |
97 | } else { |
98 | return false; |
99 | } |
100 | } |
101 | |
102 | setData(variant.constData()); |
103 | return true; |
104 | } |
105 | |
106 | void *Heap::QQmlValueTypeWrapper::storagePointer() |
107 | { |
108 | if (!gadgetPtr()) { |
109 | setGadgetPtr(::operator new(metaType().sizeOf())); |
110 | metaType().construct(where: gadgetPtr(), copy: nullptr); |
111 | } |
112 | return gadgetPtr(); |
113 | } |
114 | |
115 | bool Heap::QQmlValueTypeWrapper::readReference() |
116 | { |
117 | // If locations are enforced we only read once |
118 | return enforcesLocation() || QV4::ReferenceObject::readReference(ref: this); |
119 | } |
120 | |
121 | bool Heap::QQmlValueTypeWrapper::writeBack(int propertyIndex) |
122 | { |
123 | return isAttachedToProperty() && QV4::ReferenceObject::writeBack(ref: this, internalIndex: propertyIndex); |
124 | } |
125 | |
126 | ReturnedValue QQmlValueTypeWrapper::create( |
127 | ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, Heap::Object *object) |
128 | { |
129 | QV4::Scope scope(engine); |
130 | initProto(v4: engine); |
131 | |
132 | // Either we're enforcing the location, then we have to read right away. |
133 | // Or we don't then we lazy-load. In neither case we pass any data. |
134 | Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>( |
135 | args: nullptr, args: cloneFrom->metaType(), args: cloneFrom->metaObject(), |
136 | args&: object, args: cloneFrom->property(), args: cloneFrom->flags())); |
137 | r->d()->setLocation(function: cloneFrom->function(), statement: cloneFrom->statementIndex()); |
138 | if (cloneFrom->enforcesLocation()) |
139 | QV4::ReferenceObject::readReference(ref: r->d()); |
140 | return r->asReturnedValue(); |
141 | } |
142 | |
143 | void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4) |
144 | { |
145 | if (v4->valueTypeWrapperPrototype()->d_unchecked()) |
146 | return; |
147 | |
148 | Scope scope(v4); |
149 | ScopedObject o(scope, v4->newObject()); |
150 | o->defineDefaultProperty(name: v4->id_toString(), code: method_toString, argumentCount: 1); |
151 | v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d(); |
152 | } |
153 | |
154 | int QQmlValueTypeWrapper::virtualMetacall( |
155 | Object *object, QMetaObject::Call call, int index, void **a) |
156 | { |
157 | QQmlValueTypeWrapper *wrapper = object->as<QQmlValueTypeWrapper>(); |
158 | Q_ASSERT(wrapper); |
159 | |
160 | switch (call) { |
161 | case QMetaObject::InvokeMetaMethod: |
162 | case QMetaObject::ReadProperty: |
163 | case QMetaObject::BindableProperty: |
164 | case QMetaObject::CustomCall: |
165 | if (wrapper->d()->object()) |
166 | wrapper->d()->readReference(); |
167 | break; |
168 | default: |
169 | break; |
170 | } |
171 | |
172 | const QMetaObject *mo = wrapper->d()->metaObject(); |
173 | if (!mo->d.static_metacall) |
174 | return 0; |
175 | |
176 | mo->d.static_metacall(static_cast<QObject *>(wrapper->d()->gadgetPtr()), call, index, a); |
177 | |
178 | switch (call) { |
179 | case QMetaObject::ReadProperty: |
180 | break; |
181 | case QMetaObject::WriteProperty: |
182 | case QMetaObject::ResetProperty: |
183 | if (wrapper->d()->object()) |
184 | wrapper->d()->writeBack(propertyIndex: index); |
185 | break; |
186 | case QMetaObject::InvokeMetaMethod: |
187 | case QMetaObject::CustomCall: |
188 | if (wrapper->d()->object()) |
189 | wrapper->d()->writeBack(); |
190 | break; |
191 | default: |
192 | break; |
193 | } |
194 | |
195 | return -1; |
196 | } |
197 | |
198 | ReturnedValue QQmlValueTypeWrapper::create( |
199 | ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type, |
200 | Heap::Object *object, int property, Heap::ReferenceObject::Flags flags) |
201 | { |
202 | Scope scope(engine); |
203 | initProto(v4: engine); |
204 | |
205 | if (!type.isValid()) { |
206 | return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type" ) |
207 | .arg(args: QString::fromUtf8(utf8: type.name()))); |
208 | } |
209 | |
210 | // If data is given explicitly, we assume it has just been read from the property |
211 | Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>( |
212 | args&: data, args&: type, args&: metaObject, args&: object, args&: property, args&: flags)); |
213 | if (CppStackFrame *frame = engine->currentStackFrame) |
214 | r->d()->setLocation(function: frame->v4Function, statement: frame->statementNumber()); |
215 | if (!data && r->d()->enforcesLocation()) |
216 | QV4::ReferenceObject::readReference(ref: r->d()); |
217 | return r->asReturnedValue(); |
218 | } |
219 | |
220 | ReturnedValue QQmlValueTypeWrapper::create( |
221 | ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type) |
222 | { |
223 | Scope scope(engine); |
224 | initProto(v4: engine); |
225 | |
226 | if (!type.isValid()) { |
227 | return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type" ) |
228 | .arg(args: QString::fromUtf8(utf8: type.name()))); |
229 | } |
230 | |
231 | Scoped<QQmlValueTypeWrapper> r( |
232 | scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>( |
233 | args&: data, args&: type, args&: metaObject, args: nullptr, args: -1, args: Heap::ReferenceObject::NoFlag)); |
234 | return r->asReturnedValue(); |
235 | } |
236 | |
237 | QVariant QQmlValueTypeWrapper::toVariant() const |
238 | { |
239 | if (d()->isReference() && !readReferenceValue()) |
240 | return QVariant(); |
241 | return d()->toVariant(); |
242 | } |
243 | |
244 | bool QQmlValueTypeWrapper::toGadget(void *data) const |
245 | { |
246 | if (d()->isReference() && !readReferenceValue()) |
247 | return false; |
248 | const QMetaType type = d()->metaType(); |
249 | type.destruct(data); |
250 | type.construct(where: data, copy: d()->gadgetPtr()); |
251 | return true; |
252 | } |
253 | |
254 | bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other) |
255 | { |
256 | Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other); |
257 | QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m); |
258 | |
259 | if (QV4::VariantObject *rv = other->as<VariantObject>()) |
260 | return lv->isEqual(value: rv->d()->data()); |
261 | |
262 | if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>()) |
263 | return lv->isEqual(value: v->toVariant()); |
264 | |
265 | return false; |
266 | } |
267 | |
268 | bool QQmlValueTypeWrapper::virtualHasProperty(const Managed *m, PropertyKey id) |
269 | { |
270 | if (!id.isString()) |
271 | return Object::virtualHasProperty(m, id); |
272 | Q_ASSERT(m && m->as<QQmlValueTypeWrapper>()); |
273 | auto wrapper = static_cast<const QQmlValueTypeWrapper *>(m); |
274 | if (auto mo = wrapper->d()->metaObject()) |
275 | if (mo->indexOfProperty(name: id.toQString().toUtf8()) != -1) |
276 | return true; |
277 | |
278 | /* we don't want to fallback to QObject::virtualHasProperty |
279 | as that would end up calling getOwnProperty which is wasteful, |
280 | as it calls our own virtualGetOwnProperty. |
281 | As we know that our own properties are only those found on the meta-object, |
282 | we can instead skip the call, and simply check whether the property exists |
283 | on the prototype. |
284 | */ |
285 | Scope scope(m->engine()); |
286 | ScopedObject o(scope, m); |
287 | o = o->getPrototypeOf(); |
288 | if (o) |
289 | return o->hasProperty(id); |
290 | |
291 | return false; |
292 | } |
293 | |
294 | static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index) |
295 | { |
296 | return metaObject->property(index).isWritable() |
297 | ? (Heap::ReferenceObject::CanWriteBack | Heap::ReferenceObject::EnforcesLocation) |
298 | : Heap::ReferenceObject::EnforcesLocation; |
299 | } |
300 | |
301 | static void doStaticReadCall( |
302 | const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper, |
303 | int index, void **args) |
304 | { |
305 | metaObject->d.static_metacall( |
306 | reinterpret_cast<QObject*>( |
307 | valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, index, args); |
308 | } |
309 | |
310 | static ReturnedValue getGadgetProperty(ExecutionEngine *engine, |
311 | Heap::QQmlValueTypeWrapper *valueTypeWrapper, |
312 | QMetaType metaType, quint16 coreIndex, bool isFunction, bool isEnum) |
313 | { |
314 | if (isFunction) { |
315 | // calling a Q_INVOKABLE function of a value type |
316 | return QV4::QObjectMethod::create(scope: engine->rootContext(), valueType: valueTypeWrapper, index: coreIndex); |
317 | } |
318 | |
319 | const QMetaObject *metaObject = valueTypeWrapper->metaObject(); |
320 | int index = coreIndex; |
321 | |
322 | const auto wrapChar16 = [engine](char16_t c) { |
323 | return engine->newString(s: QChar(c)); |
324 | }; |
325 | const auto wrapQObject = [engine](QObject *object) { |
326 | return QObjectWrapper::wrap(engine, object); |
327 | }; |
328 | const auto wrapJsonValue = [engine](const QJsonValue &value) { |
329 | return JsonObject::fromJsonValue(engine, value); |
330 | }; |
331 | const auto wrapJsonObject = [engine](const QJsonObject &object) { |
332 | return JsonObject::fromJsonObject(engine, object); |
333 | }; |
334 | const auto wrapJsonArray = [engine](const QJsonArray &array) { |
335 | return JsonObject::fromJsonArray(engine, array); |
336 | }; |
337 | |
338 | const auto wrapQDateTime = [&](const QDateTime &dateTime) { |
339 | return engine->newDateObject( |
340 | dateTime, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index)); |
341 | }; |
342 | const auto wrapQDate = [&](QDate date) { |
343 | return engine->newDateObject( |
344 | date, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index)); |
345 | }; |
346 | const auto wrapQTime = [&](QTime time) { |
347 | return engine->newDateObject( |
348 | time, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index)); |
349 | }; |
350 | |
351 | #if QT_CONFIG(qml_locale) |
352 | const auto wrapLocale = [engine](const QLocale &locale) { |
353 | return QQmlLocale::wrap(engine, locale); |
354 | }; |
355 | #endif |
356 | |
357 | #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ |
358 | case metatype: { \ |
359 | cpptype v; \ |
360 | void *args[] = { &v, nullptr }; \ |
361 | doStaticReadCall(metaObject, valueTypeWrapper, index, args); \ |
362 | return QV4::Encode(constructor(v)); \ |
363 | } |
364 | |
365 | QQmlMetaObject::resolveGadgetMethodOrPropertyIndex( |
366 | type: QMetaObject::ReadProperty, metaObject: &metaObject, index: &index); |
367 | |
368 | const int metaTypeId = isEnum |
369 | ? metaType.underlyingType().id() |
370 | : (metaType.flags() & QMetaType::PointerToQObject) |
371 | ? QMetaType::QObjectStar |
372 | : metaType.id(); |
373 | |
374 | switch (metaTypeId) { |
375 | case QMetaType::UnknownType: |
376 | case QMetaType::Void: |
377 | return Encode::undefined(); |
378 | case QMetaType::Nullptr: |
379 | case QMetaType::VoidStar: |
380 | return Encode::null(); |
381 | VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); |
382 | VALUE_TYPE_LOAD(QMetaType::Int, int, int); |
383 | VALUE_TYPE_LOAD(QMetaType::UInt, uint, uint); |
384 | VALUE_TYPE_LOAD(QMetaType::Long, long, double); |
385 | VALUE_TYPE_LOAD(QMetaType::ULong, ulong, double); |
386 | VALUE_TYPE_LOAD(QMetaType::LongLong, qlonglong, double); |
387 | VALUE_TYPE_LOAD(QMetaType::ULongLong, qulonglong, double); |
388 | VALUE_TYPE_LOAD(QMetaType::Double, double, double); |
389 | VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString); |
390 | VALUE_TYPE_LOAD(QMetaType::QByteArray, QByteArray, engine->newArrayBuffer); |
391 | VALUE_TYPE_LOAD(QMetaType::Float, float, float); |
392 | VALUE_TYPE_LOAD(QMetaType::Short, short, int); |
393 | VALUE_TYPE_LOAD(QMetaType::UShort, unsigned short, int); |
394 | VALUE_TYPE_LOAD(QMetaType::Char, char, int); |
395 | VALUE_TYPE_LOAD(QMetaType::UChar, unsigned char, int); |
396 | VALUE_TYPE_LOAD(QMetaType::SChar, signed char, int); |
397 | VALUE_TYPE_LOAD(QMetaType::QChar, QChar, engine->newString); |
398 | VALUE_TYPE_LOAD(QMetaType::Char16, char16_t, wrapChar16); |
399 | VALUE_TYPE_LOAD(QMetaType::QDateTime, QDateTime, wrapQDateTime); |
400 | VALUE_TYPE_LOAD(QMetaType::QDate, QDate, wrapQDate); |
401 | VALUE_TYPE_LOAD(QMetaType::QTime, QTime, wrapQTime); |
402 | #if QT_CONFIG(regularexpression) |
403 | VALUE_TYPE_LOAD(QMetaType::QRegularExpression, QRegularExpression, engine->newRegExpObject); |
404 | #endif |
405 | VALUE_TYPE_LOAD(QMetaType::QObjectStar, QObject*, wrapQObject); |
406 | VALUE_TYPE_LOAD(QMetaType::QJsonValue, QJsonValue, wrapJsonValue); |
407 | VALUE_TYPE_LOAD(QMetaType::QJsonObject, QJsonObject, wrapJsonObject); |
408 | VALUE_TYPE_LOAD(QMetaType::QJsonArray, QJsonArray, wrapJsonArray); |
409 | #if QT_CONFIG(qml_locale) |
410 | VALUE_TYPE_LOAD(QMetaType::QLocale, QLocale, wrapLocale); |
411 | #endif |
412 | case QMetaType::QPixmap: |
413 | case QMetaType::QImage: { |
414 | QVariant v(metaType); |
415 | void *args[] = { v.data(), nullptr }; |
416 | doStaticReadCall(metaObject, valueTypeWrapper, index, args); |
417 | return Encode(engine->newVariantObject(type: metaType, data: v.data())); |
418 | } |
419 | case QMetaType::QVariant: { |
420 | QVariant v; |
421 | void *args[] = { &v, nullptr }; |
422 | doStaticReadCall(metaObject, valueTypeWrapper, index, args); |
423 | return engine->fromVariant( |
424 | variant: v, parent: valueTypeWrapper, property: index, |
425 | flags: referenceFlags(metaObject, index) | Heap::ReferenceObject::IsVariant); |
426 | } |
427 | default: |
428 | break; |
429 | } |
430 | |
431 | QVariant v(metaType); |
432 | void *args[] = { v.data(), nullptr }; |
433 | doStaticReadCall(metaObject, valueTypeWrapper, index, args); |
434 | return engine->fromVariant(variant: v, parent: valueTypeWrapper, property: index, flags: referenceFlags(metaObject, index)); |
435 | #undef VALUE_TYPE_LOAD |
436 | } |
437 | |
438 | PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
439 | { |
440 | if (id.isString()) { |
441 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
442 | Q_ASSERT(r); |
443 | |
444 | const QQmlPropertyData result = r->dataForPropertyKey(id); |
445 | if (!result.isValid()) |
446 | return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it. |
447 | |
448 | if (!p) |
449 | return Attr_Data; // Property exists, but we're not interested in the value |
450 | |
451 | if (!r->d()->isReference() || r->readReferenceValue()) { |
452 | // Property exists, and we can retrieve it |
453 | p->value = getGadgetProperty( |
454 | engine: r->engine(), valueTypeWrapper: r->d(), metaType: result.propType(), coreIndex: result.coreIndex(), |
455 | isFunction: result.isFunction(), isEnum: result.isEnum()); |
456 | } else { |
457 | // Property exists, but we can't retrieve it. Make it undefined. |
458 | p->value = Encode::undefined(); |
459 | } |
460 | |
461 | return Attr_Data; |
462 | } |
463 | |
464 | return QV4::Object::virtualGetOwnProperty(m, id, p); |
465 | } |
466 | |
467 | struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator |
468 | { |
469 | int propertyIndex = 0; |
470 | ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default; |
471 | PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; |
472 | |
473 | }; |
474 | |
475 | PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { |
476 | const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o); |
477 | |
478 | if (that->d()->isReference() && !that->readReferenceValue()) |
479 | return PropertyKey::invalid(); |
480 | |
481 | const QMetaObject *mo = that->d()->metaObject(); |
482 | // We don't return methods, ie. they are not visible when iterating |
483 | const int propertyCount = mo->propertyCount(); |
484 | if (propertyIndex < propertyCount) { |
485 | Scope scope(that->engine()); |
486 | QMetaProperty p = mo->property(index: propertyIndex); // TODO: Implement and use QBasicMetaProperty |
487 | ScopedString propName(scope, that->engine()->newString(s: QString::fromUtf8(utf8: p.name()))); |
488 | ++propertyIndex; |
489 | if (attrs) |
490 | *attrs = QV4::Attr_Data; |
491 | if (pd) { |
492 | QQmlPropertyData data; |
493 | data.load(p); |
494 | pd->value = getGadgetProperty(engine: that->engine(), valueTypeWrapper: that->d(), metaType: data.propType(), coreIndex: data.coreIndex(), isFunction: data.isFunction(), isEnum: data.isEnum()); |
495 | } |
496 | return propName->toPropertyKey(); |
497 | } |
498 | |
499 | return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); |
500 | } |
501 | |
502 | |
503 | OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) |
504 | { |
505 | *target = *m; |
506 | return new QQmlValueTypeWrapperOwnPropertyKeyIterator; |
507 | } |
508 | |
509 | bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const |
510 | { |
511 | if (d()->isReference() && !readReferenceValue()) |
512 | return false; |
513 | int id1 = value.metaType().id(); |
514 | QVariant v = d()->toVariant(); |
515 | int id2 = v.metaType().id(); |
516 | if (id1 != id2) { |
517 | // conversions for weak comparison |
518 | switch (id1) { |
519 | case QMetaType::QPoint: |
520 | if (id2 == QMetaType::QPointF) |
521 | return value.value<QPointF>() == v.value<QPointF>(); |
522 | break; |
523 | case QMetaType::QPointF: |
524 | if (id2 == QMetaType::QPoint) |
525 | return value.value<QPointF>() == v.value<QPointF>(); |
526 | break; |
527 | case QMetaType::QRect: |
528 | if (id2 == QMetaType::QRectF) |
529 | return value.value<QRectF>() == v.value<QRectF>(); |
530 | break; |
531 | case QMetaType::QRectF: |
532 | if (id2 == QMetaType::QRect) |
533 | return value.value<QRectF>() == v.value<QRectF>(); |
534 | break; |
535 | case QMetaType::QLine: |
536 | if (id2 == QMetaType::QLineF) |
537 | return value.value<QLineF>() == v.value<QLineF>(); |
538 | break; |
539 | case QMetaType::QLineF: |
540 | if (id2 == QMetaType::QLine) |
541 | return value.value<QLineF>() == v.value<QLineF>(); |
542 | break; |
543 | case QMetaType::QSize: |
544 | if (id2 == QMetaType::QSizeF) |
545 | return value.value<QSizeF>() == v.value<QSizeF>(); |
546 | break; |
547 | case QMetaType::QSizeF: |
548 | if (id2 == QMetaType::QSize) |
549 | return value.value<QSizeF>() == v.value<QSizeF>(); |
550 | break; |
551 | default: |
552 | break; |
553 | } |
554 | } |
555 | return (value == v); |
556 | } |
557 | |
558 | int QQmlValueTypeWrapper::typeId() const |
559 | { |
560 | return d()->metaType().id(); |
561 | } |
562 | |
563 | QMetaType QQmlValueTypeWrapper::type() const |
564 | { |
565 | return d()->metaType(); |
566 | } |
567 | |
568 | bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const |
569 | { |
570 | bool destructGadgetOnExit = false; |
571 | Q_ALLOCA_DECLARE(void, gadget); |
572 | if (d()->isReference()) { |
573 | if (!d()->gadgetPtr()) { |
574 | Q_ALLOCA_ASSIGN(void, gadget, d()->metaType().sizeOf()); |
575 | d()->setGadgetPtr(gadget); |
576 | d()->metaType().construct(where: d()->gadgetPtr(), copy: nullptr); |
577 | destructGadgetOnExit = true; |
578 | } |
579 | if (!readReferenceValue()) |
580 | return false; |
581 | } |
582 | |
583 | int flags = 0; |
584 | int status = -1; |
585 | void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags }; |
586 | QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a); |
587 | |
588 | if (destructGadgetOnExit) { |
589 | d()->metaType().destruct(data: d()->gadgetPtr()); |
590 | d()->setGadgetPtr(nullptr); |
591 | } |
592 | return true; |
593 | } |
594 | |
595 | QQmlPropertyData QQmlValueTypeWrapper::dataForPropertyKey(PropertyKey id) const |
596 | { |
597 | if (!id.isStringOrSymbol()) |
598 | return QQmlPropertyData {}; |
599 | QByteArray name = id.asStringOrSymbol()->toQString().toUtf8(); |
600 | const QMetaObject *mo = d()->metaObject(); |
601 | QQmlPropertyData result; |
602 | QMetaMethod metaMethod = QMetaObjectPrivate::firstMethod(baseObject: mo, name); |
603 | if (metaMethod.isValid()) { |
604 | result.load(metaMethod); |
605 | } else { |
606 | int propertyIndex = d()->metaObject()->indexOfProperty(name: name.constData()); |
607 | if (propertyIndex >= 0) |
608 | result.load(mo->property(index: propertyIndex)); |
609 | } |
610 | return result; |
611 | } |
612 | |
613 | ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
614 | { |
615 | const Object *o = thisObject->as<Object>(); |
616 | if (!o) |
617 | return b->engine()->throwTypeError(); |
618 | const QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); |
619 | if (!w) |
620 | return b->engine()->throwTypeError(); |
621 | |
622 | if (w->d()->isReference() && !w->readReferenceValue()) |
623 | RETURN_UNDEFINED(); |
624 | |
625 | QString result; |
626 | if (!QMetaType::convert(fromType: w->d()->metaType(), from: w->d()->gadgetPtr(), |
627 | toType: QMetaType(QMetaType::QString), to: &result)) { |
628 | result = QString::fromUtf8(utf8: w->d()->metaType().name()) + QLatin1Char('('); |
629 | const QMetaObject *mo = w->d()->metaObject(); |
630 | const int propCount = mo->propertyCount(); |
631 | for (int i = 0; i < propCount; ++i) { |
632 | if (mo->property(index: i).isDesignable()) { |
633 | QVariant value = mo->property(index: i).readOnGadget(gadget: w->d()->gadgetPtr()); |
634 | if (i > 0) |
635 | result += QLatin1String(", " ); |
636 | result += value.toString(); |
637 | } |
638 | } |
639 | result += QLatin1Char(')'); |
640 | } |
641 | return Encode(b->engine()->newString(s: result)); |
642 | } |
643 | |
644 | ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, |
645 | Lookup *lookup) |
646 | { |
647 | PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit-> |
648 | runtimeStrings[lookup->nameIndex]); |
649 | if (!id.isString()) |
650 | return Object::virtualResolveLookupGetter(object, engine, lookup); |
651 | |
652 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object); |
653 | QV4::ExecutionEngine *v4 = r->engine(); |
654 | Scope scope(v4); |
655 | ScopedString name(scope, id.asStringOrSymbol()); |
656 | |
657 | // Note: readReferenceValue() can change the reference->type. |
658 | if (r->d()->isReference() && !r->readReferenceValue()) |
659 | return Value::undefinedValue().asReturnedValue(); |
660 | |
661 | QQmlPropertyData result = r->dataForPropertyKey(id); |
662 | if (!result.isValid()) |
663 | return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); |
664 | |
665 | lookup->qgadgetLookup.ic = r->internalClass(); |
666 | // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h |
667 | lookup->qgadgetLookup.metaObject = quintptr(r->d()->metaObject()) + 1; |
668 | lookup->qgadgetLookup.metaType = result.propType().iface(); |
669 | lookup->qgadgetLookup.coreIndex = result.coreIndex(); |
670 | lookup->qgadgetLookup.isFunction = result.isFunction(); |
671 | lookup->qgadgetLookup.isEnum = result.isEnum(); |
672 | lookup->getter = QQmlValueTypeWrapper::lookupGetter; |
673 | return lookup->getter(lookup, engine, *object); |
674 | } |
675 | |
676 | ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) |
677 | { |
678 | const auto revertLookup = [lookup, engine, &object]() { |
679 | lookup->qgadgetLookup.metaObject = quintptr(0); |
680 | lookup->getter = Lookup::getterGeneric; |
681 | return Lookup::getterGeneric(l: lookup, engine, object); |
682 | }; |
683 | |
684 | // we can safely cast to a QV4::Object here. If object is something else, |
685 | // the internal class won't match |
686 | Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); |
687 | if (!o || o->internalClass != lookup->qgadgetLookup.ic) |
688 | return revertLookup(); |
689 | |
690 | Heap::QQmlValueTypeWrapper *valueTypeWrapper = |
691 | const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o)); |
692 | if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1)) |
693 | return revertLookup(); |
694 | |
695 | if (valueTypeWrapper->isReference() && !valueTypeWrapper->readReference()) |
696 | return Encode::undefined(); |
697 | |
698 | return getGadgetProperty( |
699 | engine, valueTypeWrapper, metaType: QMetaType(lookup->qgadgetLookup.metaType), |
700 | coreIndex: lookup->qgadgetLookup.coreIndex, isFunction: lookup->qgadgetLookup.isFunction, |
701 | isEnum: lookup->qgadgetLookup.isEnum); |
702 | } |
703 | |
704 | bool QQmlValueTypeWrapper::lookupSetter( |
705 | Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) |
706 | { |
707 | return QV4::Lookup::setterFallback(l, engine, object, value); |
708 | } |
709 | |
710 | bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, |
711 | const Value &value) |
712 | { |
713 | return Object::virtualResolveLookupSetter(object, engine, lookup, value); |
714 | } |
715 | |
716 | ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
717 | { |
718 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
719 | |
720 | if (!id.isString()) |
721 | return Object::virtualGet(m, id, receiver, hasProperty); |
722 | |
723 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
724 | QV4::ExecutionEngine *v4 = r->engine(); |
725 | |
726 | // Note: readReferenceValue() can change the reference->type. |
727 | if (r->d()->isReference() && !r->readReferenceValue()) |
728 | return Value::undefinedValue().asReturnedValue(); |
729 | |
730 | QQmlPropertyData result = r->dataForPropertyKey(id); |
731 | if (!result.isValid()) |
732 | return Object::virtualGet(m, id, receiver, hasProperty); |
733 | |
734 | if (hasProperty) |
735 | *hasProperty = true; |
736 | |
737 | return getGadgetProperty(engine: v4, valueTypeWrapper: r->d(), metaType: result.propType(), coreIndex: result.coreIndex(), isFunction: result.isFunction(), isEnum: result.isEnum()); |
738 | } |
739 | |
740 | bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
741 | { |
742 | if (!id.isString()) |
743 | return Object::virtualPut(m, id, value, receiver); |
744 | |
745 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
746 | ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); |
747 | Scope scope(v4); |
748 | if (scope.hasException()) |
749 | return false; |
750 | |
751 | Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); |
752 | Heap::Object *heapObject = nullptr; |
753 | if (r->d()->isReference()) { |
754 | heapObject = r->d()->object(); |
755 | if (!r->readReferenceValue() || !r->d()->canWriteBack()) |
756 | return false; |
757 | } |
758 | |
759 | const QMetaObject *metaObject = r->d()->metaObject(); |
760 | const QQmlPropertyData pd = r->dataForPropertyKey(id); |
761 | if (!pd.isValid()) |
762 | return false; |
763 | |
764 | if (heapObject) { |
765 | QObject *referenceObject = nullptr; |
766 | QV4::ScopedFunctionObject f(scope, value); |
767 | const int referencePropertyIndex = r->d()->property(); |
768 | QV4::Scoped<QV4::QObjectWrapper> o(scope, heapObject); |
769 | if (o) { |
770 | referenceObject = o->object(); |
771 | } else { |
772 | QV4::Scoped<QV4::QQmlTypeWrapper> t(scope, heapObject); |
773 | if (t) |
774 | referenceObject = t->object(); |
775 | } |
776 | |
777 | if (f) { |
778 | if (!f->isBinding()) { |
779 | // assigning a JS function to a non-var-property is not allowed. |
780 | QString error = QStringLiteral("Cannot assign JavaScript function to value-type property" ); |
781 | ScopedString e(scope, v4->newString(s: error)); |
782 | v4->throwError(value: e); |
783 | return false; |
784 | } |
785 | |
786 | if (!referenceObject) { |
787 | QString error = QStringLiteral("Cannot create binding on nested value type property" ); |
788 | ScopedString e(scope, v4->newString(s: error)); |
789 | v4->throwError(value: e); |
790 | return false; |
791 | } |
792 | |
793 | const QMetaProperty writebackProperty |
794 | = referenceObject->metaObject()->property(index: referencePropertyIndex); |
795 | const QMetaType writeBackPropertyType = writebackProperty.metaType(); |
796 | |
797 | QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); |
798 | |
799 | QQmlPropertyData cacheData; |
800 | cacheData.setWritable(true); |
801 | cacheData.setPropType(writeBackPropertyType); |
802 | cacheData.setCoreIndex(referencePropertyIndex); |
803 | |
804 | QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); |
805 | |
806 | QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); |
807 | QV4::ScopedContext ctx(scope, f->scope()); |
808 | QQmlBinding *newBinding = QQmlBinding::create(property: &cacheData, function: f->function(), obj: referenceObject, ctxt: context, scope: ctx); |
809 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
810 | if (f->isBoundFunction()) |
811 | newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer())); |
812 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
813 | newBinding->setTarget(referenceObject, cacheData, valueType: &pd); |
814 | QQmlPropertyPrivate::setBinding(binding: newBinding); |
815 | return true; |
816 | } else if (referenceObject) { |
817 | if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { |
818 | if (auto binding = QQmlPropertyPrivate::binding(referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) { |
819 | Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding); |
820 | const auto qmlBinding = static_cast<const QQmlBinding*>(binding); |
821 | const auto stackFrame = v4->currentStackFrame; |
822 | qCInfo(lcBindingRemoval, |
823 | "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d" , |
824 | referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(), |
825 | qPrintable(qmlBinding->expressionIdentifier()), |
826 | metaObject->property(pd.coreIndex()).name(), |
827 | qPrintable(stackFrame->source()), stackFrame->lineNumber()); |
828 | } |
829 | } |
830 | QQmlPropertyPrivate::removeBinding(o: referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex())); |
831 | } |
832 | } |
833 | |
834 | QMetaProperty property = metaObject->property(index: pd.coreIndex()); |
835 | Q_ASSERT(property.isValid()); |
836 | if (value.isUndefined() && pd.isResettable()) { |
837 | property.resetOnGadget(gadget: reinterpret_cast<QObject *>(r->d()->gadgetPtr())); |
838 | if (heapObject) |
839 | r->d()->writeBack(propertyIndex: pd.coreIndex()); |
840 | return true; |
841 | } |
842 | |
843 | QVariant v = QV4::ExecutionEngine::toVariant(value, typeHint: property.metaType()); |
844 | |
845 | if (property.isEnumType() && (QMetaType::Type)v.userType() == QMetaType::Double) |
846 | v = v.toInt(); |
847 | |
848 | void *gadget = r->d()->gadgetPtr(); |
849 | property.writeOnGadget(gadget, value: std::move(v)); |
850 | |
851 | if (heapObject) |
852 | r->d()->writeBack(propertyIndex: pd.coreIndex()); |
853 | |
854 | return true; |
855 | } |
856 | |
857 | } // namespace QV4 |
858 | |
859 | QT_END_NAMESPACE |
860 | |