Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmltypewrapper.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/qjsvalue_p.h>
7
8#include <private/qqmlcontext_p.h>
9#include <private/qqmlengine_p.h>
10#include <private/qqmlmetaobject_p.h>
11#include <private/qqmltypedata_p.h>
12#include <private/qqmlvaluetypewrapper_p.h>
13
14#include <private/qv4dateobject_p.h>
15#include <private/qv4identifiertable_p.h>
16#include <private/qv4lookup_p.h>
17#include <private/qv4objectproto_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qv4symbol_p.h>
20#include <private/qv4urlobject_p.h>
21#include <private/qv4variantobject_p.h>
22
24
25using namespace QV4;
26
30
31
32void Heap::QQmlTypeWrapper::init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type)
33{
34 Q_ASSERT(type);
35 FunctionObject::init();
36 flags = quint8(m) | quint8(Type);
37 object.init(o);
38 QQmlType::refHandle(type);
39 t.typePrivate = type;
40}
41
42void Heap::QQmlTypeWrapper::init(
43 TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import)
44{
45 Q_ASSERT(type);
46 FunctionObject::init();
47 flags = quint8(m) | quint8(Namespace);
48 object.init(o);
49 n.typeNamespace = type;
50 n.typeNamespace->addref();
51 n.importNamespace = import;
52}
53
54void Heap::QQmlTypeWrapper::destroy()
55{
56 switch (kind()) {
57 case Type:
58 Q_ASSERT(t.typePrivate);
59 QQmlType::derefHandle(t.typePrivate);
60 delete[] t.constructors;
61 break;
62 case Namespace:
63 Q_ASSERT(n.typeNamespace);
64 n.typeNamespace->release();
65 break;
66 }
67
68 object.destroy();
69 FunctionObject::destroy();
70}
71
72QQmlType Heap::QQmlTypeWrapper::type() const
73{
74 switch (kind()) {
75 case Type:
76 return QQmlType(t.typePrivate);
77 case Namespace:
78 return QQmlType();
79 }
80
81 Q_UNREACHABLE_RETURN(QQmlType());
82}
83
84QQmlTypeNameCache::Result Heap::QQmlTypeWrapper::queryNamespace(
85 const QV4::String *name, QV4::ExecutionEngine *engine) const
86{
87 Q_ASSERT(kind() == Namespace);
88 Q_ASSERT(n.typeNamespace);
89 Q_ASSERT(n.importNamespace);
90 return n.typeNamespace->query(name, n.importNamespace, engine->typeLoader());
91
92}
93
94template<typename Callback>
95void warnWithLocation(const Heap::QQmlTypeWrapper *wrapper, Callback &&callback)
96{
97 auto log = qWarning().noquote().nospace();
98 if (const CppStackFrame *frame = wrapper->internalClass->engine->currentStackFrame)
99 log << frame->source() << ':' << frame->lineNumber() << ':';
100 callback(log.space());
101}
102
103void Heap::QQmlTypeWrapper::warnIfUncreatable() const
104{
105 const QQmlType t = type();
106 Q_ASSERT(t.isValid());
107
108 if (t.isValueType())
109 return;
110
111 if (t.isSingleton()) {
112 warnWithLocation(this, [&](QDebug &log) {
113 log << "You are calling a Q_INVOKABLE constructor of" << t.typeName()
114 << "which is a singleton in QML.";
115 });
116 return;
117 }
118
119 if (!t.isCreatable()) {
120 warnWithLocation(this, [&](QDebug &log) {
121 log << "You are calling a Q_INVOKABLE constructor of" << t.typeName()
122 << "which is uncreatable in QML.";
123 });
124 }
125}
126
127bool QQmlTypeWrapper::isSingleton() const
128{
129 return d()->type().isSingleton();
130}
131
132const QMetaObject *QQmlTypeWrapper::metaObject() const
133{
134 const QQmlType type = d()->type();
135 if (!type.isValid())
136 return nullptr;
137
138 if (type.isSingleton()) {
139 auto metaObjectCandidate = type.metaObject();
140 // if the candidate is the same as te baseMetaObject, we know that
141 // we don't have an extended singleton; in that case the
142 // actual instance might be subclass of type instead of type itself
143 // so we need to query the actual object for it's meta-object
144 if (metaObjectCandidate == type.baseMetaObject()) {
145 QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
146 auto object = qmlEngine->singletonInstance<QObject *>(type);
147 if (object)
148 return object->metaObject();
149 }
150 /* if we instead have an extended singleton, the dynamic proxy
151 meta-object must alreday be set up correctly
152 ### TODO: it isn't, as QQmlTypePrivate::init has no way to
153 query the object
154 */
155 return metaObjectCandidate;
156 }
157
158 return type.attachedPropertiesType(engine()->typeLoader());
159}
160
161QObject *QQmlTypeWrapper::object() const
162{
163 const QQmlType type = d()->type();
164 if (!type.isValid())
165 return nullptr;
166
167 if (type.isSingleton())
168 return QQmlEnginePrivate::get(engine()->qmlEngine())->singletonInstance<QObject *>(type);
169
170 return qmlAttachedPropertiesObject(
171 d()->object,
172 type.attachedPropertiesFunction(engine()->typeLoader()));
173}
174
175QObject* QQmlTypeWrapper::singletonObject() const
176{
177 if (!isSingleton())
178 return nullptr;
179
180 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
181 return e->singletonInstance<QObject*>(d()->type());
182}
183
184QVariant QQmlTypeWrapper::toVariant() const
185{
186 const QQmlType type = d()->type();
187
188 // A QQmlTypeWrapper can represent a type namespace.
189 // In that case there is no sensible C++ representation. Wrap a QJSValue.
190 if (!type.isValid())
191 return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(asReturnedValue()));
192
193 if (!isSingleton()) {
194 return QVariant::fromValue(qmlAttachedPropertiesObject(
195 d()->object, type.attachedPropertiesFunction(engine()->typeLoader())));
196 }
197
198 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
199 if (type.isQJSValueSingleton())
200 return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type));
201
202 return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type));
203}
204
205ReturnedValue QQmlTypeWrapper::method_hasInstance(
206 const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
207{
208 // we want to immediately call instanceOf rather than going through Function
209
210 if (!argc)
211 return Encode(false);
212 if (const Object *o = thisObject->as<Object>())
213 return o->instanceOf(argv[0]);
214 return Encode(false);
215}
216
217ReturnedValue QQmlTypeWrapper::method_toString(
218 const FunctionObject *b, const Value *thisObject, const Value *, int)
219{
220 const QQmlTypeWrapper *typeWrapper = thisObject->as<QQmlTypeWrapper>();
221 if (!typeWrapper)
222 RETURN_UNDEFINED();
223
224 const QString name = typeWrapper->d()->type().qmlTypeName();
225 return Encode(b->engine()->newString(name.isEmpty()
226 ? QLatin1String("Unknown Type")
227 : name));
228}
229
230void QQmlTypeWrapper::initProto(ExecutionEngine *v4)
231{
232 if (v4->typeWrapperPrototype()->d_unchecked())
233 return;
234
235 Scope scope(v4);
236 ScopedObject o(scope, v4->newObject());
237
238 o->defineDefaultProperty(v4->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly);
239 o->defineDefaultProperty(v4->id_toString(), method_toString, 0);
240 o->setPrototypeOf(v4->functionPrototype());
241
242 v4->jsObjects[QV4::ExecutionEngine::TypeWrapperProto] = o->d();
243}
244
245// Returns a type wrapper for type t on o. This allows access of enums, and attached properties.
246ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t,
247 Heap::QQmlTypeWrapper::TypeNameMode mode)
248{
249 Q_ASSERT(t.isValid());
250 initProto(engine);
251
252 QV4::MemoryManager *mm = engine->memoryManager;
253
254 if (const QMetaObject *mo = t.metaObject(); !mo || mo->constructorCount() == 0)
255 return mm->allocate<QQmlTypeWrapper>(mode, o, t.priv())->asReturnedValue();
256
257 return mm->allocate<QQmlTypeConstructor>(mode, o, t.priv())->asReturnedValue();
258}
259
260// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a
261// namespace.
262ReturnedValue QQmlTypeWrapper::create(
263 QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t,
264 const QQmlImportRef *importNamespace, Heap::QQmlTypeWrapper::TypeNameMode mode)
265{
266 Q_ASSERT(t);
267 Q_ASSERT(importNamespace);
268 initProto(engine);
269
270 Scope scope(engine);
271
272 Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>(
273 mode, o, t.data(), importNamespace));
274 return w.asReturnedValue();
275}
276
277static int enumForSingleton(QQmlTypeLoader *typeLoader, String *name, const QQmlType &type, bool *ok)
278{
279 Q_ASSERT(ok != nullptr);
280 const int value = type.enumValue(typeLoader, name, ok);
281 return *ok ? value : -1;
282}
283
284static ReturnedValue createEnumWrapper(ExecutionEngine *v4, Scope &scope, QQmlType type,
285 int enumIndex, bool scoped)
286{
287 Scoped<QQmlEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlEnumWrapper>());
288 enumWrapper->d()->typePrivate = type.priv();
289 QQmlType::refHandle(enumWrapper->d()->typePrivate);
290 enumWrapper->d()->enumIndex = enumIndex;
291 enumWrapper->d()->scoped = scoped;
292 return enumWrapper.asReturnedValue();
293}
294
295ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
296{
297 // Keep this code in sync with ::virtualResolveLookupGetter
298 Q_ASSERT(m->as<QQmlTypeWrapper>());
299
300 if (!id.isString())
301 return Object::virtualGet(m, id, receiver, hasProperty);
302
303 QV4::ExecutionEngine *v4 = m->engine();
304 QV4::Scope scope(v4);
305 ScopedString name(scope, id.asStringOrSymbol());
306
307 Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m));
308
309 if (hasProperty)
310 *hasProperty = true;
311
312 QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext();
313
314 QObject *object = w->d()->object;
315 QQmlType type = w->d()->type();
316
317 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4->qmlEngine());
318 if (type.isValid()) {
319
320 // singleton types are handled differently to other types.
321 if (type.isSingleton()) {
322
323 if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
324 if (QObject *qobjectSingleton = enginePrivate->singletonInstance<QObject*>(type)) {
325 // check for enum value
326 const bool includeEnums
327 = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums;
328 if (includeEnums && name->startsWithUpper()) {
329 bool ok = false;
330 int value = enumForSingleton(v4->typeLoader(), name, type, &ok);
331 if (ok)
332 return QV4::Value::fromInt32(value).asReturnedValue();
333
334 value = type.scopedEnumIndex(v4->typeLoader(), name, &ok);
335 if (ok)
336 return createEnumWrapper(v4, scope, type, value, true);
337
338 value = type.unscopedEnumIndex(v4->typeLoader(), name, &ok);
339 if (ok)
340 return createEnumWrapper(v4, scope, type, value, false);
341 }
342
343 // check for property.
344 bool ok;
345 const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(
346 v4, context, w->d(), qobjectSingleton, name,
347 QV4::QObjectWrapper::AttachMethods, &ok);
348 if (hasProperty)
349 *hasProperty = ok;
350
351 return result;
352 }
353 } else if (type.isQJSValueSingleton()) {
354 QJSValue scriptSingleton = enginePrivate->singletonInstance<QJSValue>(type);
355 if (!scriptSingleton.isUndefined()) {
356 // NOTE: if used in a binding, changes will not trigger re-evaluation since non-bindable.
357 QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton));
358 if (!!o)
359 return o->get(name);
360 }
361 }
362
363 // Fall through to base implementation
364
365 } else {
366
367 if (name->startsWithUpper()) {
368 bool ok = false;
369 int value = type.enumValue(v4->typeLoader(), name, &ok);
370 if (ok)
371 return QV4::Value::fromInt32(value).asReturnedValue();
372
373 value = type.scopedEnumIndex(v4->typeLoader(), name, &ok);
374 if (ok)
375 return createEnumWrapper(v4, scope, type, value, true);
376
377 value = type.unscopedEnumIndex(v4->typeLoader(), name, &ok);
378 if (ok)
379 return createEnumWrapper(v4, scope, type, value, false);
380
381 // Fall through to base implementation
382
383 } else if (w->d()->object) {
384 QObject *ao = qmlAttachedPropertiesObject(
385 object, type.attachedPropertiesFunction(v4->typeLoader()));
386 if (ao)
387 return QV4::QObjectWrapper::getQmlProperty(
388 v4, context, w->d(), ao, name, QV4::QObjectWrapper::AttachMethods,
389 hasProperty);
390
391 // Fall through to base implementation
392 }
393
394 // Fall through to base implementation
395 }
396
397 // Fall through to base implementation
398
399 } else if (w->d()->kind() == Heap::QQmlTypeWrapper::Namespace) {
400 const QQmlTypeNameCache::Result r = w->d()->queryNamespace(name, v4);
401 if (r.isValid()) {
402 if (r.type.isValid()) {
403 return create(scope.engine, object, r.type, w->d()->typeNameMode());
404 } else if (r.scriptIndex != -1) {
405 QV4::ScopedObject scripts(scope, context->importedScripts());
406 return scripts->get(r.scriptIndex);
407 } else if (r.importNamespace) {
408 return create(scope.engine, object, context->imports(), r.importNamespace);
409 }
410
411 return QV4::Encode::undefined();
412
413 }
414
415 // Fall through to base implementation
416
417 } else {
418 Q_ASSERT(!"Unreachable");
419 }
420
421 bool ok = false;
422 const ReturnedValue result = Object::virtualGet(m, id, receiver, &ok);
423 if (hasProperty)
424 *hasProperty = ok;
425
426 return result;
427}
428
429
430bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
431{
432 if (!id.isString())
433 return Object::virtualPut(m, id, value, receiver);
434
435
436 Q_ASSERT(m->as<QQmlTypeWrapper>());
437 QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m);
438 QV4::Scope scope(w);
439 if (scope.hasException())
440 return false;
441
442 ScopedString name(scope, id.asStringOrSymbol());
443 QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext();
444
445 QQmlType type = w->d()->type();
446 if (type.isValid() && !type.isSingleton() && w->d()->object) {
447 QObject *object = w->d()->object;
448 QObject *ao = qmlAttachedPropertiesObject(
449 object, type.attachedPropertiesFunction(scope.engine->typeLoader()));
450 if (ao)
451 return QV4::QObjectWrapper::setQmlProperty(
452 scope.engine, context, ao, name, QV4::QObjectWrapper::NoFlag, value);
453 return false;
454 } else if (type.isSingleton()) {
455 QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine());
456 if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
457 if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type))
458 return QV4::QObjectWrapper::setQmlProperty(
459 scope.engine, context, qobjectSingleton, name,
460 QV4::QObjectWrapper::NoFlag, value);
461
462 } else {
463 QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type);
464 if (!scriptSingleton.isUndefined()) {
465 QV4::ScopedObject apiprivate(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton));
466 if (!apiprivate) {
467 QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
468 scope.engine->throwError(error);
469 return false;
470 } else {
471 return apiprivate->put(name, value);
472 }
473 }
474 }
475 }
476
477 return false;
478}
479
480PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
481{
482 if (id.isString()) {
483 Scope scope(m);
484 ScopedString n(scope, id.asStringOrSymbol());
485 // ### Implement more efficiently.
486 bool hasProperty = false;
487 static_cast<const Object *>(m)->get(n, &hasProperty);
488 return hasProperty ? Attr_Data : Attr_Invalid;
489 }
490
491 return QV4::Object::virtualGetOwnProperty(m, id, p);
492}
493
494bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b)
495{
496 Q_ASSERT(a->as<QV4::QQmlTypeWrapper>());
497 QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a);
498 if (QV4::QQmlTypeWrapper *qmlTypeWrapperB = b->as<QV4::QQmlTypeWrapper>())
499 return qmlTypeWrapperA->toVariant() == qmlTypeWrapperB->toVariant();
500 else if (QV4::QObjectWrapper *qobjectWrapper = b->as<QV4::QObjectWrapper>())
501 return qmlTypeWrapperA->toVariant().value<QObject*>() == qobjectWrapper->object();
502
503 return false;
504}
505
507 const QV4::QQmlTypeWrapper *typeWrapper, QObject *wrapperObject)
508{
509 QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine;
510 // in case the wrapper outlived the QObject*
511 if (!wrapperObject)
512 return engine->throwTypeError();
513
514 const QQmlType type = typeWrapper->d()->type();
515 const QMetaType myTypeId = type.typeId();
516 QQmlMetaObject myQmlType;
517 if (!myTypeId.isValid()) {
518 // we're a composite type; a composite type cannot be equal to a
519 // non-composite object instance (Rectangle{} is never an instance of
520 // CustomRectangle)
521 QQmlData *theirDData = QQmlData::get(wrapperObject);
522 Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?!
523 if (!theirDData->compilationUnit)
524 return Encode(false);
525
526 QQmlRefPointer<QQmlTypeData> td
527 = engine->typeLoader()->getType(typeWrapper->d()->type().sourceUrl());
528 if (CompiledData::CompilationUnit *cu = td->compilationUnit())
529 myQmlType = QQmlMetaType::metaObjectForType(cu->metaType());
530 else
531 return Encode(false); // It seems myQmlType has some errors, so we could not compile it.
532 } else {
533 myQmlType = QQmlMetaType::metaObjectForType(myTypeId);
534 if (myQmlType.isNull())
535 return Encode(false);
536 }
537
538 const QMetaObject *theirType = wrapperObject->metaObject();
539
540 if (QQmlMetaObject::canConvert(theirType, myQmlType))
541 return Encode(true);
542 else if (type.isValueType())
543 return Encode::undefined();
544 else
545 return Encode(false);
546}
547
548ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
549{
550 Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
551 const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
552
553 if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>())
554 return instanceOfQObject(typeWrapper, objectWrapper->object());
555
556 if (const QQmlTypeWrapper *varTypeWrapper = var.as<QQmlTypeWrapper>()) {
557 // Singleton or attachment
558 if (QObject *varObject = varTypeWrapper->object())
559 return instanceOfQObject(typeWrapper, varObject);
560 }
561
562 const QQmlType type = typeWrapper->d()->type();
563
564 // If the target type is an object type we want null.
565 if (!type.isValueType())
566 return Encode(false);
567
568 const auto canCastValueType = [&]() -> bool {
569 if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) {
570 return QQmlMetaObject::canConvert(
571 valueWrapper->metaObject(), type.metaObjectForValueType());
572 }
573
574 const QMetaType typeId = type.typeId();
575 if (const VariantObject *variantObject = var.as<VariantObject>()) {
576 if (variantObject->d()->data().metaType() == typeId)
577 return true;
578 }
579
580 switch (typeId.id()) {
581 case QMetaType::Void:
582 return var.isUndefined();
583 case QMetaType::QVariant:
584 return true; // Everything is a var
585 case QMetaType::Int:
586 return var.isInteger();
587 case QMetaType::Double:
588 return var.isDouble(); // Integers are also doubles
589 case QMetaType::QString:
590 return var.isString();
591 case QMetaType::Bool:
592 return var.isBoolean();
593 case QMetaType::QUrl:
594 if (var.as<UrlObject>())
595 return true;
596 break;
597 case QMetaType::QDate:
598 case QMetaType::QTime:
599 case QMetaType::QDateTime:
600 if (var.as<DateObject>())
601 return true;
602 break;
603 default:
604 break;
605 }
606
607 return false;
608 };
609
610 // We want "foo as valuetype" to return undefined if it doesn't match.
611 return canCastValueType() ? Encode(true) : Encode::undefined();
612}
613
614ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
615{
616 // Keep this code in sync with ::virtualGet
617 PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
618 if (!id.isString())
619 return Object::virtualResolveLookupGetter(object, engine, lookup);
620 Scope scope(engine);
621
622 const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object);
623 ScopedString name(scope, id.asStringOrSymbol());
624 QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext();
625
626 Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This));
627 QQmlType type = w->d()->type();
628
629 if (type.isValid()) {
630
631 if (type.isSingleton()) {
632 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
633 if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
634 if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
635 const bool includeEnums
636 = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums;
637 if (!includeEnums || !name->startsWithUpper()) {
638 QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
639 if (ddata && ddata->propertyCache) {
640 const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
641 if (property) {
642 ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
643 if (qualifiesForMethodLookup(property)) {
644 QV4::Heap::QObjectMethod *method = nullptr;
645 setupQObjectMethodLookup(
646 lookup, ddata->propertyCache, property,
647 val->objectValue(), method);
648 lookup->call = QV4::Lookup::Call::GetterSingletonMethod;
649 } else {
650 setupQObjectLookup(
651 lookup, ddata, property, val->objectValue(), This);
652 lookup->call = QV4::Lookup::Call::GetterSingletonProperty;
653 }
654 return lookup->getter(engine, *object);
655 }
656 // Fall through to base implementation
657 }
658 // Fall through to base implementation
659 }
660 // Fall through to base implementation
661 }
662 // Fall through to base implementation
663 }
664 // Fall through to base implementation
665 }
666
667 if (name->startsWithUpper()) {
668 bool ok = false;
669 QQmlTypeLoader *typeLoader = engine->typeLoader();
670 int value = type.enumValue(typeLoader, name, &ok);
671 if (ok) {
672 lookup->qmlEnumValueLookup.ic.set(engine, This->internalClass());
673 lookup->qmlEnumValueLookup.encodedEnumValue
674 = QV4::Value::fromInt32(value).asReturnedValue();
675 lookup->call = QV4::Lookup::Call::GetterEnumValue;
676 return lookup->getter(engine, *object);
677 }
678
679 value = type.scopedEnumIndex(typeLoader, name, &ok);
680 if (ok) {
681 Scoped<QQmlEnumWrapper> enumWrapper(
682 scope, createEnumWrapper(engine, scope, type, value, true));
683 auto *wrapper = enumWrapper->as<QQmlEnumWrapper>();
684 lookup->qmlEnumWrapperLookup.ic.set(engine, This->internalClass());
685 lookup->qmlEnumWrapperLookup.qmlEnumWrapper.set(engine, wrapper->heapObject());
686 lookup->call = QV4::Lookup::Call::GetterEnum;
687 return enumWrapper.asReturnedValue();
688 }
689
690 value = type.unscopedEnumIndex(typeLoader, name, &ok);
691 if (ok) {
692 Scoped<QQmlEnumWrapper> enumWrapper(
693 scope, createEnumWrapper(engine, scope, type, value, false));
694 auto *wrapper = enumWrapper->as<QQmlEnumWrapper>();
695 lookup->qmlEnumWrapperLookup.ic.set(engine, This->internalClass());
696 lookup->qmlEnumWrapperLookup.qmlEnumWrapper.set(engine, wrapper->heapObject());
697 lookup->call = QV4::Lookup::Call::GetterEnum;
698 return enumWrapper.asReturnedValue();
699 }
700
701 // Fall through to base implementation
702 } else if (w->d()->object) {
703 QObject *ao = qmlAttachedPropertiesObject(
704 w->d()->object, type.attachedPropertiesFunction(engine->typeLoader()));
705 if (ao) {
706 // ### QTBUG-126877: Optimize this case
707 lookup->call = QV4::Lookup::Call::GetterQObjectPropertyFallback;
708 return lookup->getter(engine, *object);
709 }
710 }
711 // Fall through to base implementation
712 }
713 /* ### QTBUG-126877: use QV4::Object::virtualResolveLookupGetter once we can be sure
714 that we don't run into issues related to Function being our prototype */
715 lookup->call = QV4::Lookup::Call::GetterQObjectPropertyFallback;
716 return lookup->getter(engine, *object);
717}
718
719bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
720{
721 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
722}
723
724OwnPropertyKeyIterator *QQmlTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
725{
726 QV4::Scope scope(m->engine());
727 QV4::Scoped<QQmlTypeWrapper> typeWrapper(scope, m);
728 Q_ASSERT(typeWrapper);
729 if (QObject *object = typeWrapper->object()) {
730 QV4::Scoped<QV4::QObjectWrapper> objectWrapper(scope, QV4::QObjectWrapper::wrap(typeWrapper->engine(), object));
731 return QV4::QObjectWrapper::virtualOwnPropertyKeys(objectWrapper, target);
732 }
733
734 return Object::virtualOwnPropertyKeys(m, target);
735}
736
737int QQmlTypeWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
738{
739 QQmlTypeWrapper *wrapper = object->as<QQmlTypeWrapper>();
740 Q_ASSERT(wrapper);
741
742 if (QObject *qObject = wrapper->object())
743 return QMetaObject::metacall(qObject, call, index, a);
744
745 return 0;
746}
747
748ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object)
749{
750 const auto revertLookup = [l, engine, &object]() {
751 l->qobjectLookup.propertyCache->release();
752 l->qobjectLookup.propertyCache = nullptr;
753 l->call = QV4::Lookup::Call::GetterGeneric;
754 return Lookup::getterGeneric(l, engine, object);
755 };
756
757 // we can safely cast to a QV4::Object here. If object is something else,
758 // the internal class won't match
759 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
760
761 // The qmlTypeIc check is not strictly necessary.
762 // If we have different ways to get to the same QObject type
763 // we can use the same lookup to get its properties, no matter
764 // how we've found the object. Most of the few times this check
765 // fails, we will, of course have different object types. So
766 // this check provides an early exit for the error case.
767 //
768 // So, if we ever need more bits in qobjectLookup, qmlTypeIc is the
769 // member to be replaced.
770 if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc)
771 return revertLookup();
772
773 Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o);
774
775 QQmlType type = This->type();
776 if (!type.isValid())
777 return revertLookup();
778
779 if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
780 return revertLookup();
781
782 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
783 QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
784 Q_ASSERT(qobjectSingleton);
785
786 Scope scope(engine);
787 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
788 const QObjectWrapper::Flags flags = l->forCall
789 ? QObjectWrapper::AllowOverride
790 : (QObjectWrapper::AttachMethods | QObjectWrapper::AllowOverride);
791 return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, revertLookup);
792}
793
794ReturnedValue QQmlTypeWrapper::lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &object)
795{
796 const auto revertLookup = [l, engine, &object]() {
797 l->qobjectMethodLookup.propertyCache->release();
798 l->qobjectMethodLookup.propertyCache = nullptr;
799 l->call = QV4::Lookup::Call::GetterGeneric;
800 return Lookup::getterGeneric(l, engine, object);
801 };
802
803 // We cannot safely cast here as we don't explicitly check the IC. Therefore as().
804 const QQmlTypeWrapper *This = object.as<QQmlTypeWrapper>();
805 if (!This)
806 return revertLookup();
807
808 QQmlType type = This->d()->type();
809 if (!type.isValid())
810 return revertLookup();
811
812 if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
813 return revertLookup();
814
815 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
816 QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
817 Q_ASSERT(qobjectSingleton);
818
819 Scope scope(engine);
820 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
821 return QObjectWrapper::lookupMethodGetterImpl(
822 l, engine, obj, l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods,
823 revertLookup);
824}
825
826ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base)
827{
828 auto *o = static_cast<Heap::Object *>(base.heapObject());
829 if (!o || o->internalClass != l->qmlEnumValueLookup.ic) {
830 l->call = QV4::Lookup::Call::GetterGeneric;
831 return Lookup::getterGeneric(l, engine, base);
832 }
833
834 return l->qmlEnumValueLookup.encodedEnumValue;
835}
836
837ReturnedValue QQmlTypeWrapper::lookupEnum(Lookup *l, ExecutionEngine *engine, const Value &base)
838{
839 Scope scope(engine);
840 Scoped<QQmlEnumWrapper> enumWrapper(scope, l->qmlEnumWrapperLookup.qmlEnumWrapper.get());
841
842 auto *o = static_cast<Heap::Object *>(base.heapObject());
843 if (!o || o->internalClass != l->qmlEnumWrapperLookup.ic) {
844 QQmlType::derefHandle(enumWrapper->d()->typePrivate);
845 l->qmlEnumWrapperLookup.qmlEnumWrapper.clear();
846 l->call = QV4::Lookup::Call::GetterGeneric;
847 return Lookup::getterGeneric(l, engine, base);
848 }
849
850 return enumWrapper.asReturnedValue();
851}
852
853void Heap::QQmlEnumWrapper::destroy()
854{
855 QQmlType::derefHandle(typePrivate);
856 typePrivate = nullptr;
857 Object::destroy();
858}
859
860QQmlType Heap::QQmlEnumWrapper::type() const
861{
862 return QQmlType(typePrivate);
863}
864
865ReturnedValue QQmlEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver,
866 bool *hasProperty)
867{
868 Q_ASSERT(m->as<QQmlEnumWrapper>());
869 if (!id.isString())
870 return Object::virtualGet(m, id, receiver, hasProperty);
871
872 const QQmlEnumWrapper *resource = static_cast<const QQmlEnumWrapper *>(m);
873 QV4::ExecutionEngine *v4 = resource->engine();
874 QV4::Scope scope(v4);
875 ScopedString name(scope, id.asStringOrSymbol());
876
877 QQmlType type = resource->d()->type();
878 int index = resource->d()->enumIndex;
879
880 bool ok = false;
881 auto *typeLoader = v4->typeLoader();
882 int value = resource->d()->scoped ? type.scopedEnumValue(typeLoader, index, name, &ok)
883 : type.unscopedEnumValue(typeLoader, index, name, &ok);
884 if (hasProperty)
885 *hasProperty = ok;
886 if (ok)
887 return QV4::Value::fromInt32(value).asReturnedValue();
888
889 return Object::virtualGet(m, id, receiver, hasProperty);
890}
891
892QT_END_NAMESPACE
Combined button and popup list for selecting options.
static ReturnedValue createEnumWrapper(ExecutionEngine *v4, Scope &scope, QQmlType type, int enumIndex, bool scoped)
static int enumForSingleton(QQmlTypeLoader *typeLoader, String *name, const QQmlType &type, bool *ok)
DEFINE_OBJECT_VTABLE(QQmlTypeWrapper)
static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, QObject *wrapperObject)
DEFINE_OBJECT_VTABLE(QQmlTypeConstructor)
void warnWithLocation(const Heap::QQmlTypeWrapper *wrapper, Callback &&callback)