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