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 (gadgetPtr) { |
100 | valueType->metaType.destruct(gadgetPtr); |
101 | ::operator delete(gadgetPtr); |
102 | } |
103 | if (_propertyCache) |
104 | _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 (gadgetPtr) |
112 | valueType->metaType.destruct(gadgetPtr); |
113 | if (!gadgetPtr) |
114 | gadgetPtr = ::operator new(valueType->metaType.sizeOf()); |
115 | valueType->metaType.construct(gadgetPtr, 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(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(variantReferenceType)) { |
146 | QQmlPropertyCache *cache = nullptr; |
147 | if (const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(variantReferenceType)) |
148 | cache = QJSEnginePrivate::get(engine())->cache(mo); |
149 | if (d()->gadgetPtr) { |
150 | d()->valueType->metaType.destruct(d()->gadgetPtr); |
151 | ::operator delete(d()->gadgetPtr); |
152 | } |
153 | d()->gadgetPtr =nullptr; |
154 | d()->setPropertyCache(cache); |
155 | d()->valueType = QQmlValueTypeFactory::valueType(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()->gadgetPtr = ::operator new(d()->valueType->metaType.sizeOf()); |
166 | d()->valueType->metaType.construct(d()->gadgetPtr, 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(v4->id_toString(), method_toString, 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(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(engine)->cache(metaObject)); |
195 | r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); |
196 | r->d()->gadgetPtr = nullptr; |
197 | return r->asReturnedValue(); |
198 | } |
199 | |
200 | ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVariant &value, const QMetaObject *metaObject, int typeId) |
201 | { |
202 | Scope scope(engine); |
203 | initProto(engine); |
204 | |
205 | Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); |
206 | r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject)); |
207 | r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); |
208 | r->d()->gadgetPtr = nullptr; |
209 | r->d()->setValue(value); |
210 | return r->asReturnedValue(); |
211 | } |
212 | |
213 | QVariant QQmlValueTypeWrapper::toVariant() const |
214 | { |
215 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
216 | if (!ref->readReferenceValue()) |
217 | return QVariant(); |
218 | return d()->toVariant(); |
219 | } |
220 | |
221 | bool QQmlValueTypeWrapper::toGadget(void *data) const |
222 | { |
223 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
224 | if (!ref->readReferenceValue()) |
225 | return false; |
226 | const int typeId = d()->valueType->metaType.id(); |
227 | QMetaType::destruct(typeId, data); |
228 | QMetaType::construct(typeId, data, d()->gadgetPtr); |
229 | return true; |
230 | } |
231 | |
232 | bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other) |
233 | { |
234 | Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other); |
235 | QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m); |
236 | |
237 | if (QV4::VariantObject *rv = other->as<VariantObject>()) |
238 | return lv->isEqual(rv->d()->data()); |
239 | |
240 | if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>()) |
241 | return lv->isEqual(v->toVariant()); |
242 | |
243 | return false; |
244 | } |
245 | |
246 | PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
247 | { |
248 | if (id.isString()) { |
249 | Scope scope(m); |
250 | ScopedString n(scope, id.asStringOrSymbol()); |
251 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
252 | QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr); |
253 | return result ? Attr_Data : Attr_Invalid; |
254 | } |
255 | |
256 | return QV4::Object::virtualGetOwnProperty(m, id, p); |
257 | } |
258 | |
259 | struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator |
260 | { |
261 | int propertyIndex = 0; |
262 | ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default; |
263 | PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; |
264 | |
265 | }; |
266 | |
267 | PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { |
268 | const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o); |
269 | |
270 | if (const QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) { |
271 | if (!ref->readReferenceValue()) |
272 | return PropertyKey::invalid(); |
273 | } |
274 | |
275 | if (that->d()->propertyCache()) { |
276 | const QMetaObject *mo = that->d()->propertyCache()->createMetaObject(); |
277 | const int propertyCount = mo->propertyCount(); |
278 | if (propertyIndex < propertyCount) { |
279 | Scope scope(that->engine()); |
280 | ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(propertyIndex).name()))); |
281 | ++propertyIndex; |
282 | if (attrs) |
283 | *attrs = QV4::Attr_Data; |
284 | if (pd) |
285 | pd->value = that->QV4::Object::get(propName); |
286 | return propName->toPropertyKey(); |
287 | } |
288 | } |
289 | |
290 | return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); |
291 | } |
292 | |
293 | |
294 | OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) |
295 | { |
296 | *target = *m; |
297 | return new QQmlValueTypeWrapperOwnPropertyKeyIterator; |
298 | } |
299 | |
300 | bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const |
301 | { |
302 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) |
303 | if (!ref->readReferenceValue()) |
304 | return false; |
305 | return (value == d()->toVariant()); |
306 | } |
307 | |
308 | int QQmlValueTypeWrapper::typeId() const |
309 | { |
310 | return d()->valueType->metaType.id(); |
311 | } |
312 | |
313 | bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const |
314 | { |
315 | bool destructGadgetOnExit = false; |
316 | Q_ALLOCA_DECLARE(void, gadget); |
317 | if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) { |
318 | if (!d()->gadgetPtr) { |
319 | Q_ALLOCA_ASSIGN(void, gadget, d()->valueType->metaType.sizeOf()); |
320 | d()->gadgetPtr = gadget; |
321 | d()->valueType->metaType.construct(d()->gadgetPtr, nullptr); |
322 | destructGadgetOnExit = true; |
323 | } |
324 | if (!ref->readReferenceValue()) |
325 | return false; |
326 | } |
327 | |
328 | int flags = 0; |
329 | int status = -1; |
330 | void *a[] = { d()->gadgetPtr, nullptr, &status, &flags }; |
331 | QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a); |
332 | |
333 | if (destructGadgetOnExit) { |
334 | d()->valueType->metaType.destruct(d()->gadgetPtr); |
335 | d()->gadgetPtr = nullptr; |
336 | } |
337 | return true; |
338 | } |
339 | |
340 | ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
341 | { |
342 | const Object *o = thisObject->as<Object>(); |
343 | if (!o) |
344 | return b->engine()->throwTypeError(); |
345 | const QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); |
346 | if (!w) |
347 | return b->engine()->throwTypeError(); |
348 | |
349 | if (const QQmlValueTypeReference *ref = w->as<QQmlValueTypeReference>()) |
350 | if (!ref->readReferenceValue()) |
351 | RETURN_UNDEFINED(); |
352 | |
353 | QString result; |
354 | // Prepare a buffer to pass to QMetaType::convert() |
355 | QString convertResult; |
356 | convertResult.~QString(); |
357 | if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->metaType.id(), &convertResult, QMetaType::QString)) { |
358 | result = convertResult; |
359 | } else { |
360 | result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->metaType.id())) |
361 | + QLatin1Char('('); |
362 | const QMetaObject *mo = w->d()->propertyCache()->metaObject(); |
363 | const int propCount = mo->propertyCount(); |
364 | for (int i = 0; i < propCount; ++i) { |
365 | if (mo->property(i).isDesignable()) { |
366 | QVariant value = mo->property(i).readOnGadget(w->d()->gadgetPtr); |
367 | if (i > 0) |
368 | result += QLatin1String(", " ); |
369 | result += value.toString(); |
370 | } |
371 | } |
372 | result += QLatin1Char(')'); |
373 | } |
374 | return Encode(b->engine()->newString(result)); |
375 | } |
376 | |
377 | Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine, |
378 | Heap::QQmlValueTypeWrapper *valueTypeWrapper, |
379 | QQmlPropertyData *property) |
380 | { |
381 | if (property->isFunction()) { |
382 | // calling a Q_INVOKABLE function of a value type |
383 | return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex()); |
384 | } |
385 | |
386 | #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ |
387 | if (property->propType() == metatype) { \ |
388 | cpptype v; \ |
389 | void *args[] = { &v, nullptr }; \ |
390 | metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \ |
391 | QMetaObject::ReadProperty, index, args); \ |
392 | return QV4::Encode(constructor(v)); \ |
393 | } |
394 | |
395 | const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject(); |
396 | |
397 | int index = property->coreIndex(); |
398 | QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); |
399 | |
400 | // These four types are the most common used by the value type wrappers |
401 | VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); |
402 | VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int); |
403 | VALUE_TYPE_LOAD(QMetaType::Int, int, int); |
404 | VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString); |
405 | VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); |
406 | |
407 | QVariant v; |
408 | void *args[] = { nullptr, nullptr }; |
409 | if (property->propType() == QMetaType::QVariant) { |
410 | args[0] = &v; |
411 | } else { |
412 | v = QVariant(property->propType(), static_cast<void *>(nullptr)); |
413 | args[0] = v.data(); |
414 | } |
415 | metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty, |
416 | index, args); |
417 | return engine->fromVariant(v); |
418 | #undef VALUE_TYPE_LOAD |
419 | } |
420 | |
421 | ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, |
422 | Lookup *lookup) |
423 | { |
424 | PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> |
425 | runtimeStrings[lookup->nameIndex]); |
426 | if (!id.isString()) |
427 | return Object::virtualResolveLookupGetter(object, engine, lookup); |
428 | |
429 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object); |
430 | QV4::ExecutionEngine *v4 = r->engine(); |
431 | Scope scope(v4); |
432 | ScopedString name(scope, id.asStringOrSymbol()); |
433 | |
434 | // Note: readReferenceValue() can change the reference->type. |
435 | if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { |
436 | if (!reference->readReferenceValue()) |
437 | return Value::undefinedValue().asReturnedValue(); |
438 | } |
439 | |
440 | QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); |
441 | if (!result) |
442 | return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); |
443 | |
444 | lookup->qgadgetLookup.ic = r->internalClass(); |
445 | lookup->qgadgetLookup.propertyCache = r->d()->propertyCache(); |
446 | lookup->qgadgetLookup.propertyCache->addref(); |
447 | lookup->qgadgetLookup.propertyData = result; |
448 | lookup->getter = QQmlValueTypeWrapper::lookupGetter; |
449 | return lookup->getter(lookup, engine, *object); |
450 | } |
451 | |
452 | ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) |
453 | { |
454 | const auto revertLookup = [lookup, engine, &object]() { |
455 | lookup->qgadgetLookup.propertyCache->release(); |
456 | lookup->qgadgetLookup.propertyCache = nullptr; |
457 | lookup->getter = Lookup::getterGeneric; |
458 | return Lookup::getterGeneric(lookup, engine, object); |
459 | }; |
460 | |
461 | // we can safely cast to a QV4::Object here. If object is something else, |
462 | // the internal class won't match |
463 | Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); |
464 | if (!o || o->internalClass != lookup->qgadgetLookup.ic) |
465 | return revertLookup(); |
466 | |
467 | Heap::QQmlValueTypeWrapper *valueTypeWrapper = |
468 | const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o)); |
469 | if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache) |
470 | return revertLookup(); |
471 | |
472 | if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) { |
473 | Scope scope(engine); |
474 | Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper); |
475 | referenceWrapper->readReferenceValue(); |
476 | } |
477 | |
478 | QQmlPropertyData *property = lookup->qgadgetLookup.propertyData; |
479 | return getGadgetProperty(engine, valueTypeWrapper, property); |
480 | } |
481 | |
482 | bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, |
483 | const Value &value) |
484 | { |
485 | return Object::virtualResolveLookupSetter(object, engine, lookup, value); |
486 | } |
487 | |
488 | ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
489 | { |
490 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
491 | |
492 | if (!id.isString()) |
493 | return Object::virtualGet(m, id, receiver, hasProperty); |
494 | |
495 | const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); |
496 | QV4::ExecutionEngine *v4 = r->engine(); |
497 | Scope scope(v4); |
498 | ScopedString name(scope, id.asStringOrSymbol()); |
499 | |
500 | // Note: readReferenceValue() can change the reference->type. |
501 | if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { |
502 | if (!reference->readReferenceValue()) |
503 | return Value::undefinedValue().asReturnedValue(); |
504 | } |
505 | |
506 | QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); |
507 | if (!result) |
508 | return Object::virtualGet(m, id, receiver, hasProperty); |
509 | |
510 | if (hasProperty) |
511 | *hasProperty = true; |
512 | |
513 | return getGadgetProperty(v4, r->d(), result); |
514 | } |
515 | |
516 | bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
517 | { |
518 | if (!id.isString()) |
519 | return Object::virtualPut(m, id, value, receiver); |
520 | |
521 | Q_ASSERT(m->as<QQmlValueTypeWrapper>()); |
522 | ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); |
523 | Scope scope(v4); |
524 | if (scope.hasException()) |
525 | return false; |
526 | |
527 | Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); |
528 | Scoped<QQmlValueTypeReference> reference(scope, m->d()); |
529 | |
530 | int writeBackPropertyType = -1; |
531 | |
532 | if (reference) { |
533 | QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property); |
534 | |
535 | if (!writebackProperty.isWritable() || !reference->readReferenceValue()) |
536 | return false; |
537 | |
538 | writeBackPropertyType = writebackProperty.userType(); |
539 | } |
540 | |
541 | ScopedString name(scope, id.asStringOrSymbol()); |
542 | |
543 | const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); |
544 | const QQmlPropertyData *pd = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); |
545 | if (!pd) |
546 | return false; |
547 | |
548 | if (reference) { |
549 | QV4::ScopedFunctionObject f(scope, value); |
550 | const QQmlQPointer<QObject> &referenceObject = reference->d()->object; |
551 | const int referencePropertyIndex = reference->d()->property; |
552 | |
553 | if (f) { |
554 | if (!f->isBinding()) { |
555 | // assigning a JS function to a non-var-property is not allowed. |
556 | QString error = QStringLiteral("Cannot assign JavaScript function to value-type property" ); |
557 | ScopedString e(scope, v4->newString(error)); |
558 | v4->throwError(e); |
559 | return false; |
560 | } |
561 | |
562 | QQmlContextData *context = v4->callingQmlContext(); |
563 | |
564 | QQmlPropertyData cacheData; |
565 | cacheData.setWritable(true); |
566 | cacheData.setPropType(writeBackPropertyType); |
567 | cacheData.setCoreIndex(referencePropertyIndex); |
568 | |
569 | QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); |
570 | |
571 | QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); |
572 | QV4::ScopedContext ctx(scope, f->scope()); |
573 | QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx); |
574 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
575 | if (f->isBoundFunction()) |
576 | newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer())); |
577 | newBinding->setSourceLocation(bindingFunction->currentLocation()); |
578 | newBinding->setTarget(referenceObject, cacheData, pd); |
579 | QQmlPropertyPrivate::setBinding(newBinding); |
580 | return true; |
581 | } else { |
582 | if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { |
583 | if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) { |
584 | Q_ASSERT(!binding->isValueTypeProxy()); |
585 | const auto qmlBinding = static_cast<const QQmlBinding*>(binding); |
586 | const auto stackFrame = v4->currentStackFrame; |
587 | qCInfo(lcBindingRemoval, |
588 | "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d" , |
589 | referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(), |
590 | qPrintable(qmlBinding->expressionIdentifier()), |
591 | metaObject->property(pd->coreIndex()).name(), |
592 | qPrintable(stackFrame->source()), stackFrame->lineNumber()); |
593 | } |
594 | } |
595 | QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex())); |
596 | } |
597 | } |
598 | |
599 | QMetaProperty property = metaObject->property(pd->coreIndex()); |
600 | Q_ASSERT(property.isValid()); |
601 | |
602 | QVariant v = v4->toVariant(value, property.userType()); |
603 | |
604 | if (property.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double) |
605 | v = v.toInt(); |
606 | |
607 | void *gadget = r->d()->gadgetPtr; |
608 | property.writeOnGadget(gadget, v); |
609 | |
610 | |
611 | if (reference) { |
612 | if (writeBackPropertyType == QMetaType::QVariant) { |
613 | QVariant variantReferenceValue = r->d()->toVariant(); |
614 | |
615 | int flags = 0; |
616 | int status = -1; |
617 | void *a[] = { &variantReferenceValue, nullptr, &status, &flags }; |
618 | QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); |
619 | |
620 | } else { |
621 | int flags = 0; |
622 | int status = -1; |
623 | void *a[] = { r->d()->gadgetPtr, nullptr, &status, &flags }; |
624 | QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); |
625 | } |
626 | } |
627 | |
628 | return true; |
629 | } |
630 | |
631 | QT_END_NAMESPACE |
632 | |