1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmlvaluetypewrapper_p.h" |
41 | |
42 | #include <private/qqmlvaluetype_p.h> |
43 | #include <private/qqmlbinding_p.h> |
44 | #include <private/qqmlglobal_p.h> |
45 | #include <private/qqmlbuiltinfunctions_p.h> |
46 | |
47 | #include <private/qv4engine_p.h> |
48 | #include <private/qv4functionobject_p.h> |
49 | #include <private/qv4variantobject_p.h> |
50 | #include <private/qv4alloca_p.h> |
51 | #include <private/qv4stackframe_p.h> |
52 | #include <private/qv4objectiterator_p.h> |
53 | #include <private/qv4qobjectwrapper_p.h> |
54 | #include <private/qv4identifiertable_p.h> |
55 | #include <private/qv4lookup_p.h> |
56 | #include <QtCore/qloggingcategory.h> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) |
61 | |
62 | DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); |
63 | |
64 | namespace QV4 { |
65 | namespace Heap { |
66 | |
67 | struct QQmlValueTypeReference : QQmlValueTypeWrapper |
68 | { |
69 | void init() { |
70 | QQmlValueTypeWrapper::init(); |
71 | object.init(); |
72 | } |
73 | void destroy() { |
74 | object.destroy(); |
75 | QQmlValueTypeWrapper::destroy(); |
76 | } |
77 | QQmlQPointer<QObject> object; |
78 | int property; |
79 | }; |
80 | |
81 | } |
82 | |
83 | struct QQmlValueTypeReference : public QQmlValueTypeWrapper |
84 | { |
85 | V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper) |
86 | V4_NEEDS_DESTROY |
87 | |
88 | bool readReferenceValue() const; |
89 | }; |
90 | |
91 | } |
92 | |
93 | DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference); |
94 | |
95 | using namespace QV4; |
96 | |
97 | void Heap::QQmlValueTypeWrapper::destroy() |
98 | { |
99 | if (m_gadgetPtr) { |
100 | m_valueType->metaType.destruct(data: m_gadgetPtr); |
101 | ::operator delete(m_gadgetPtr); |
102 | } |
103 | if (m_propertyCache) |
104 | m_propertyCache->release(); |
105 | Object::destroy(); |
106 | } |
107 | |
108 | void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const |
109 | { |
110 | Q_ASSERT(valueType()->metaType.id() == value.userType()); |
111 | if (auto *gadget = gadgetPtr()) |
112 | valueType()->metaType.destruct(data: gadget); |
113 | if (!gadgetPtr()) |
114 | setGadgetPtr(::operator new(valueType()->metaType.sizeOf())); |
115 | valueType()->metaType.construct(where: gadgetPtr(), copy: value.constData()); |
116 | } |
117 | |
118 | QVariant Heap::QQmlValueTypeWrapper::toVariant() const |
119 | { |
120 | Q_ASSERT(gadgetPtr()); |
121 | return QVariant(valueType()->metaType.id(), gadgetPtr()); |
122 | } |
123 | |
124 | |
125 | bool QQmlValueTypeReference::readReferenceValue() const |
126 | { |
127 | if (!d()->object) |
128 | return false; |
129 | // A reference resource may be either a "true" reference (eg, to a QVector3D property) |
130 | // or a "variant" reference (eg, to a QVariant property which happens to contain a value-type). |
131 | QMetaProperty writebackProperty = d()->object->metaObject()->property(index: d()->property); |
132 | if (writebackProperty.userType() == QMetaType::QVariant) { |
133 | // variant-containing-value-type reference |
134 | QVariant variantReferenceValue; |
135 | |
136 | void *a[] = { &variantReferenceValue, nullptr }; |
137 | QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, a); |
138 | |
139 | int variantReferenceType = variantReferenceValue.userType(); |
140 | if (variantReferenceType != typeId()) { |
141 | // This is a stale VariantReference. That is, the variant has been |
142 | // overwritten with a different type in the meantime. |
143 | // We need to modify this reference to the updated value type, if |
144 | // possible, or return false if it is not a value type. |
145 | if (QQmlValueTypeFactory::isValueType(idx: variantReferenceType)) { |
146 | QQmlPropertyCache *cache = nullptr; |
147 | if (const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(type: variantReferenceType)) |
148 | cache = QJSEnginePrivate::get(e: engine())->cache(metaObject: mo); |
149 | if (d()->gadgetPtr()) { |
150 | d()->valueType()->metaType.destruct(data: d()->gadgetPtr()); |
151 | ::operator delete(d()->gadgetPtr()); |
152 | } |
153 | d()->setGadgetPtr(nullptr); |
154 | d()->setPropertyCache(cache); |
155 | d()->setValueType(QQmlValueTypeFactory::valueType(idx: variantReferenceType)); |
156 | if (!cache) |
157 | return false; |
158 | } else { |
159 | return false; |
160 | } |
161 | } |
162 | d()->setValue(variantReferenceValue); |
163 | } else { |
164 | if (!d()->gadgetPtr()) { |
165 | d()->setGadgetPtr(::operator new(d()->valueType()->metaType.sizeOf())); |
166 | d()->valueType()->metaType.construct(where: d()->gadgetPtr(), copy: nullptr); |
167 | } |
168 | // value-type reference |
169 | void *args[] = { d()->gadgetPtr(), nullptr }; |
170 | QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, args); |
171 | } |
172 | return true; |
173 | } |
174 | |
175 | void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4) |
176 | { |
177 | if (v4->valueTypeWrapperPrototype()->d_unchecked()) |
178 | return; |
179 | |
180 | Scope scope(v4); |
181 | ScopedObject o(scope, v4->newObject()); |
182 | o->defineDefaultProperty(name: v4->id_toString(), code: method_toString, argumentCount: 1); |
183 | v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d(); |
184 | } |
185 | |
186 | ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *object, int property, const QMetaObject *metaObject, int typeId) |
187 | { |
188 | Scope scope(engine); |
189 | initProto(v4: engine); |
190 | |
191 | Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>()); |
192 | r->d()->object = object; |
193 | r->d()->property = property; |
194 | r->d()->setPropertyCache(QJSEnginePrivate::get(e: engine)->cache(metaObject)); |
195 | auto valueType = QQmlValueTypeFactory::valueType(idx: typeId); |
196 | if (!valueType) { |
197 | return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type" ) |
198 | .arg(args: QString::fromUtf8(str: QMetaType(typeId).name()))); |
199 | } |
200 | r->d()->setValueType(valueType); |
201 | r->d()->setGadgetPtr(nullptr); |
202 | return r->asReturnedValue(); |
203 | } |
204 | |
205 | ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVariant &value, const QMetaObject *metaObject, int typeId) |
206 | { |
207 | Scope scope(engine); |
208 | initProto(v4: engine); |
209 | |
210 | Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); |
211 | r->d()->setPropertyCache(QJSEnginePrivate::get(e: engine)->cache(metaObject)); |
212 | auto valueType = QQmlValueTypeFactory::valueType(idx: typeId); |
213 | if (!valueType) { |
214 | return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type" ) |
215 | .arg(args: QString::fromUtf8(str: QMetaType(typeId).name()))); |
216 | } |
217 | r->d()->setValueType(valueType); |
218 | r->d()->setGadgetPtr(nullptr); |
219 | r->d()->setValue(value); |
220 | return r->asReturnedValue(); |
221 | } |
222 | |
223 | QVariant QQmlValueTypeWrapper::toVariant() const |
224 | { |
225 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
226 | if (!ref->readReferenceValue()) |
227 | return QVariant(); |
228 | return d()->toVariant(); |
229 | } |
230 | |
231 | bool QQmlValueTypeWrapper::toGadget(void *data) const |
232 | { |
233 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
234 | if (!ref->readReferenceValue()) |
235 | return false; |
236 | const int typeId = d()->valueType()->metaType.id(); |
237 | QMetaType::destruct(type: typeId, where: data); |
238 | QMetaType::construct(type: typeId, where: data, copy: d()->gadgetPtr()); |
239 | return true; |
240 | } |
241 | |
242 | bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other) |
243 | { |
244 | Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other); |
245 | QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m); |
246 | |
247 | if (QV4::VariantObject *rv = other->as<VariantObject>()) |
248 | return lv->isEqual(value: rv->d()->data()); |
249 | |
250 | if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>()) |
251 | return lv->isEqual(value: v->toVariant()); |
252 | |
253 | return false; |
254 | } |
255 | |
256 | PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
257 | { |
258 | if (id.isString()) { |
259 | Scope scope(m); |
260 | ScopedString n(scope, id.asStringOrSymbol()); |
261 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
262 | QQmlPropertyData *result = r->d()->propertyCache()->property(key: n.getPointer(), object: nullptr, context: nullptr); |
263 | return result ? Attr_Data : Attr_Invalid; |
264 | } |
265 | |
266 | return QV4::Object::virtualGetOwnProperty(m, id, p); |
267 | } |
268 | |
269 | struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator |
270 | { |
271 | int propertyIndex = 0; |
272 | ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default; |
273 | PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; |
274 | |
275 | }; |
276 | |
277 | PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { |
278 | const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o); |
279 | |
280 | if (const QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) { |
281 | if (!ref->readReferenceValue()) |
282 | return PropertyKey::invalid(); |
283 | } |
284 | |
285 | if (that->d()->propertyCache()) { |
286 | const QMetaObject *mo = that->d()->propertyCache()->createMetaObject(); |
287 | const int propertyCount = mo->propertyCount(); |
288 | if (propertyIndex < propertyCount) { |
289 | Scope scope(that->engine()); |
290 | ScopedString propName(scope, that->engine()->newString(s: QString::fromUtf8(str: mo->property(index: propertyIndex).name()))); |
291 | ++propertyIndex; |
292 | if (attrs) |
293 | *attrs = QV4::Attr_Data; |
294 | if (pd) |
295 | pd->value = that->QV4::Object::get(name: propName); |
296 | return propName->toPropertyKey(); |
297 | } |
298 | } |
299 | |
300 | return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); |
301 | } |
302 | |
303 | |
304 | OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) |
305 | { |
306 | *target = *m; |
307 | return new QQmlValueTypeWrapperOwnPropertyKeyIterator; |
308 | } |
309 | |
310 | bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const |
311 | { |
312 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
313 | if (!ref->readReferenceValue()) |
314 | return false; |
315 | return (value == d()->toVariant()); |
316 | } |
317 | |
318 | int QQmlValueTypeWrapper::typeId() const |
319 | { |
320 | return d()->valueType()->metaType.id(); |
321 | } |
322 | |
323 | bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const |
324 | { |
325 | bool destructGadgetOnExit = false; |
326 | Q_ALLOCA_DECLARE(void, gadget); |
327 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) { |
328 | if (!d()->gadgetPtr()) { |
329 | Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType.sizeOf()); |
330 | d()->setGadgetPtr(gadget); |
331 | d()->valueType()->metaType.construct(where: d()->gadgetPtr(), copy: nullptr); |
332 | destructGadgetOnExit = true; |
333 | } |
334 | if (!ref->readReferenceValue()) |
335 | return false; |
336 | } |
337 | |
338 | int flags = 0; |
339 | int status = -1; |
340 | void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags }; |
341 | QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a); |
342 | |
343 | if (destructGadgetOnExit) { |
344 | d()->valueType()->metaType.destruct(data: d()->gadgetPtr()); |
345 | d()->setGadgetPtr(nullptr); |
346 | } |
347 | return true; |
348 | } |
349 | |
350 | ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
351 | { |
352 | const Object *o = thisObject->as<Object>(); |
353 | if (!o) |
354 | return b->engine()->throwTypeError(); |
355 | const QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); |
356 | if (!w) |
357 | return b->engine()->throwTypeError(); |
358 | |
359 | if (const QQmlValueTypeReference *ref = w->as<QQmlValueTypeReference>()) |
360 | if (!ref->readReferenceValue()) |
361 | RETURN_UNDEFINED(); |
362 | |
363 | QString result; |
364 | // Prepare a buffer to pass to QMetaType::convert() |
365 | QString convertResult; |
366 | convertResult.~QString(); |
367 | if (QMetaType::convert(from: w->d()->gadgetPtr(), fromTypeId: w->d()->valueType()->metaType.id(), to: &convertResult, toTypeId: QMetaType::QString)) { |
368 | result = convertResult; |
369 | } else { |
370 | result += QString::fromUtf8(str: QMetaType::typeName(type: w->d()->valueType()->metaType.id())) |
371 | + QLatin1Char('('); |
372 | const QMetaObject *mo = w->d()->propertyCache()->metaObject(); |
373 | const int propCount = mo->propertyCount(); |
374 | for (int i = 0; i < propCount; ++i) { |
375 | if (mo->property(index: i).isDesignable()) { |
376 | QVariant value = mo->property(index: i).readOnGadget(gadget: w->d()->gadgetPtr()); |
377 | if (i > 0) |
378 | result += QLatin1String(", " ); |
379 | result += value.toString(); |
380 | } |
381 | } |
382 | result += QLatin1Char(')'); |
383 | } |
384 | return Encode(b->engine()->newString(s: result)); |
385 | } |
386 | |
387 | Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine, |
388 | Heap::QQmlValueTypeWrapper *valueTypeWrapper, |
389 | QQmlPropertyData *property) |
390 | { |
391 | if (property->isFunction()) { |
392 | // calling a Q_INVOKABLE function of a value type |
393 | return QV4::QObjectMethod::create(scope: engine->rootContext(), valueType: valueTypeWrapper, index: property->coreIndex()); |
394 | } |
395 | |
396 | #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ |
397 | if (property->propType() == metatype) { \ |
398 | cpptype v; \ |
399 | void *args[] = { &v, nullptr }; \ |
400 | metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), \ |
401 | QMetaObject::ReadProperty, index, args); \ |
402 | return QV4::Encode(constructor(v)); \ |
403 | } |
404 | |
405 | const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject(); |
406 | |
407 | int index = property->coreIndex(); |
408 | QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type: QMetaObject::ReadProperty, metaObject: &metaObject, index: &index); |
409 | |
410 | // These four types are the most common used by the value type wrappers |
411 | VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); |
412 | VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int); |
413 | VALUE_TYPE_LOAD(QMetaType::Int, int, int); |
414 | VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString); |
415 | VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); |
416 | |
417 | QVariant v; |
418 | void *args[] = { nullptr, nullptr }; |
419 | if (property->propType() == QMetaType::QVariant) { |
420 | args[0] = &v; |
421 | } else { |
422 | v = QVariant(property->propType(), static_cast<void *>(nullptr)); |
423 | args[0] = v.data(); |
424 | } |
425 | metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, |
426 | index, args); |
427 | return engine->fromVariant(v); |
428 | #undef VALUE_TYPE_LOAD |
429 | } |
430 | |
431 | ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, |
432 | Lookup *lookup) |
433 | { |
434 | PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit-> |
435 | runtimeStrings[lookup->nameIndex]); |
436 | if (!id.isString()) |
437 | return Object::virtualResolveLookupGetter(object, engine, lookup); |
438 | |
439 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object); |
440 | QV4::ExecutionEngine *v4 = r->engine(); |
441 | Scope scope(v4); |
442 | ScopedString name(scope, id.asStringOrSymbol()); |
443 | |
444 | // Note: readReferenceValue() can change the reference->type. |
445 | if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { |
446 | if (!reference->readReferenceValue()) |
447 | return Value::undefinedValue().asReturnedValue(); |
448 | } |
449 | |
450 | QQmlPropertyData *result = r->d()->propertyCache()->property(key: name.getPointer(), object: nullptr, context: nullptr); |
451 | if (!result) |
452 | return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); |
453 | |
454 | lookup->qgadgetLookup.ic = r->internalClass(); |
455 | lookup->qgadgetLookup.propertyCache = r->d()->propertyCache(); |
456 | lookup->qgadgetLookup.propertyCache->addref(); |
457 | lookup->qgadgetLookup.propertyData = result; |
458 | lookup->getter = QQmlValueTypeWrapper::lookupGetter; |
459 | return lookup->getter(lookup, engine, *object); |
460 | } |
461 | |
462 | ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) |
463 | { |
464 | const auto revertLookup = [lookup, engine, &object]() { |
465 | lookup->qgadgetLookup.propertyCache->release(); |
466 | lookup->qgadgetLookup.propertyCache = nullptr; |
467 | lookup->getter = Lookup::getterGeneric; |
468 | return Lookup::getterGeneric(l: lookup, engine, object); |
469 | }; |
470 | |
471 | // we can safely cast to a QV4::Object here. If object is something else, |
472 | // the internal class won't match |
473 | Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); |
474 | if (!o || o->internalClass != lookup->qgadgetLookup.ic) |
475 | return revertLookup(); |
476 | |
477 | Heap::QQmlValueTypeWrapper *valueTypeWrapper = |
478 | const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o)); |
479 | if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache) |
480 | return revertLookup(); |
481 | |
482 | if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) { |
483 | Scope scope(engine); |
484 | Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper); |
485 | referenceWrapper->readReferenceValue(); |
486 | } |
487 | |
488 | QQmlPropertyData *property = lookup->qgadgetLookup.propertyData; |
489 | return getGadgetProperty(engine, valueTypeWrapper, property); |
490 | } |
491 | |
492 | bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, |
493 | const Value &value) |
494 | { |
495 | return Object::virtualResolveLookupSetter(object, engine, lookup, value); |
496 | } |
497 | |
498 | ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
499 | { |
500 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
501 | |
502 | if (!id.isString()) |
503 | return Object::virtualGet(m, id, receiver, hasProperty); |
504 | |
505 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
506 | QV4::ExecutionEngine *v4 = r->engine(); |
507 | Scope scope(v4); |
508 | ScopedString name(scope, id.asStringOrSymbol()); |
509 | |
510 | // Note: readReferenceValue() can change the reference->type. |
511 | if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { |
512 | if (!reference->readReferenceValue()) |
513 | return Value::undefinedValue().asReturnedValue(); |
514 | } |
515 | |
516 | QQmlPropertyData *result = r->d()->propertyCache()->property(key: name.getPointer(), object: nullptr, context: nullptr); |
517 | if (!result) |
518 | return Object::virtualGet(m, id, receiver, hasProperty); |
519 | |
520 | if (hasProperty) |
521 | *hasProperty = true; |
522 | |
523 | return getGadgetProperty(engine: v4, valueTypeWrapper: r->d(), property: result); |
524 | } |
525 | |
526 | bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
527 | { |
528 | if (!id.isString()) |
529 | return Object::virtualPut(m, id, value, receiver); |
530 | |
531 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
532 | ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); |
533 | Scope scope(v4); |
534 | if (scope.hasException()) |
535 | return false; |
536 | |
537 | Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); |
538 | Scoped<QQmlValueTypeReference> reference(scope, m->d()); |
539 | |
540 | int writeBackPropertyType = -1; |
541 | |
542 | if (reference) { |
543 | QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(index: reference->d()->property); |
544 | |
545 | if (!writebackProperty.isWritable() || !reference->readReferenceValue()) |
546 | return false; |
547 | |
548 | writeBackPropertyType = writebackProperty.userType(); |
549 | } |
550 | |
551 | ScopedString name(scope, id.asStringOrSymbol()); |
552 | |
553 | const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); |
554 | const QQmlPropertyData *pd = r->d()->propertyCache()->property(key: name.getPointer(), object: nullptr, context: nullptr); |
555 | if (!pd) |
556 | return false; |
557 | |
558 | if (reference) { |
559 | QV4::ScopedFunctionObject f(scope, value); |
560 | const QQmlQPointer<QObject> &referenceObject = reference->d()->object; |
561 | const int referencePropertyIndex = reference->d()->property; |
562 | |
563 | if (f) { |
564 | if (!f->isBinding()) { |
565 | // assigning a JS function to a non-var-property is not allowed. |
566 | QString error = QStringLiteral("Cannot assign JavaScript function to value-type property" ); |
567 | ScopedString e(scope, v4->newString(s: error)); |
568 | v4->throwError(value: e); |
569 | return false; |
570 | } |
571 | |
572 | QQmlContextData *context = v4->callingQmlContext(); |
573 | |
574 | QQmlPropertyData cacheData; |
575 | cacheData.setWritable(true); |
576 | cacheData.setPropType(writeBackPropertyType); |
577 | cacheData.setCoreIndex(referencePropertyIndex); |
578 | |
579 | QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); |
580 | |
581 | QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); |
582 | QV4::ScopedContext ctx(scope, f->scope()); |
583 | QQmlBinding *newBinding = QQmlBinding::create(property: &cacheData, function: f->function(), obj: referenceObject, ctxt: context, scope: ctx); |
584 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
585 | if (f->isBoundFunction()) |
586 | newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer())); |
587 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
588 | newBinding->setTarget(referenceObject, cacheData, valueType: pd); |
589 | QQmlPropertyPrivate::setBinding(binding: newBinding); |
590 | return true; |
591 | } else { |
592 | if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { |
593 | if (auto binding = QQmlPropertyPrivate::binding(referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) { |
594 | Q_ASSERT(!binding->isValueTypeProxy()); |
595 | const auto qmlBinding = static_cast<const QQmlBinding*>(binding); |
596 | const auto stackFrame = v4->currentStackFrame; |
597 | qCInfo(lcBindingRemoval, |
598 | "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d" , |
599 | referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(), |
600 | qPrintable(qmlBinding->expressionIdentifier()), |
601 | metaObject->property(pd->coreIndex()).name(), |
602 | qPrintable(stackFrame->source()), stackFrame->lineNumber()); |
603 | } |
604 | } |
605 | QQmlPropertyPrivate::removeBinding(o: referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex())); |
606 | } |
607 | } |
608 | |
609 | QMetaProperty property = metaObject->property(index: pd->coreIndex()); |
610 | Q_ASSERT(property.isValid()); |
611 | |
612 | QVariant v = v4->toVariant(value, typeHint: property.userType()); |
613 | |
614 | if (property.isEnumType() && (QMetaType::Type)v.userType() == QMetaType::Double) |
615 | v = v.toInt(); |
616 | |
617 | void *gadget = r->d()->gadgetPtr(); |
618 | property.writeOnGadget(gadget, value: v); |
619 | |
620 | |
621 | if (reference) { |
622 | if (writeBackPropertyType == QMetaType::QVariant) { |
623 | QVariant variantReferenceValue = r->d()->toVariant(); |
624 | |
625 | int flags = 0; |
626 | int status = -1; |
627 | void *a[] = { &variantReferenceValue, nullptr, &status, &flags }; |
628 | QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); |
629 | |
630 | } else { |
631 | int flags = 0; |
632 | int status = -1; |
633 | void *a[] = { r->d()->gadgetPtr(), nullptr, &status, &flags }; |
634 | QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); |
635 | } |
636 | } |
637 | |
638 | return true; |
639 | } |
640 | |
641 | QT_END_NAMESPACE |
642 | |