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
507static bool instanceHasCompilationUnit(QObject *wrapperObject)
508{
509 // If the target type is a composite type, we can cut instanceof short
510 // if the object is not of a composite type (e.g. Rectangle{} is never
511 // an instance of CustomRectangle)
512
513 QQmlData *theirDData = QQmlData::get(wrapperObject);
514 Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?!
515 return !theirDData->compilationUnit.isNull();
516}
517
519 const QV4::QQmlTypeWrapper *typeWrapper, QObject *wrapperObject)
520{
521 QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine;
522 // in case the wrapper outlived the QObject*
523 if (!wrapperObject)
524 return engine->throwTypeError();
525
526 const QQmlType type = typeWrapper->d()->type();
527 QQmlMetaObject myQmlType;
528 if (type.isComposite()) {
529 if (!instanceHasCompilationUnit(wrapperObject))
530 return Encode(false);
531
532 const CompiledData::CompilationUnit *cu
533 = engine->typeLoader()->getType(type.sourceUrl())->compilationUnit();
534
535 // If the CU isn't there, the type probably has errors, so we could not compile it.
536 if (!cu)
537 return Encode(false);
538
539 myQmlType = QQmlMetaObject(cu->rootPropertyCache());
540 Q_ASSERT(!myQmlType.isNull());
541 } else if (type.isInlineComponentType()) {
542 if (!instanceHasCompilationUnit(wrapperObject))
543 return Encode(false);
544
545 auto baseUrl = type.sourceUrl();
546 baseUrl.setFragment(QString());
547
548 const CompiledData::CompilationUnit *cu
549 = engine->typeLoader()->getType(baseUrl)->compilationUnit();
550
551 if (!cu)
552 return Encode(false);
553
554 myQmlType = QQmlMetaObject(cu->propertyCaches.at(
555 cu->inlineComponentId(type.elementName())));
556 Q_ASSERT(!myQmlType.isNull());
557 } else {
558 myQmlType = QQmlMetaType::metaObjectForType(type.typeId());
559 if (myQmlType.isNull())
560 return Encode(false);
561 }
562
563 const QMetaObject *theirType = wrapperObject->metaObject();
564
565 if (QQmlMetaObject::canConvert(theirType, myQmlType))
566 return Encode(true);
567 else if (type.isValueType())
568 return Encode::undefined();
569 else
570 return Encode(false);
571}
572
573ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
574{
575 Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
576 const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
577
578 if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>())
579 return instanceOfQObject(typeWrapper, objectWrapper->object());
580
581 if (const QQmlTypeWrapper *varTypeWrapper = var.as<QQmlTypeWrapper>()) {
582 // Singleton or attachment
583 if (QObject *varObject = varTypeWrapper->object())
584 return instanceOfQObject(typeWrapper, varObject);
585 }
586
587 const QQmlType type = typeWrapper->d()->type();
588
589 // If the target type is an object type we want null.
590 if (!type.isValueType())
591 return Encode(false);
592
593 const auto canCastValueType = [&]() -> bool {
594 if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) {
595 return QQmlMetaObject::canConvert(
596 valueWrapper->metaObject(), type.metaObjectForValueType());
597 }
598
599 const QMetaType typeId = type.typeId();
600 if (const VariantObject *variantObject = var.as<VariantObject>()) {
601 if (variantObject->d()->data().metaType() == typeId)
602 return true;
603 }
604
605 switch (typeId.id()) {
606 case QMetaType::Void:
607 return var.isUndefined();
608 case QMetaType::QVariant:
609 return true; // Everything is a var
610 case QMetaType::Int:
611 return var.isInteger();
612 case QMetaType::Double:
613 return var.isDouble(); // Integers are also doubles
614 case QMetaType::QString:
615 return var.isString();
616 case QMetaType::Bool:
617 return var.isBoolean();
618 case QMetaType::QUrl:
619 if (var.as<UrlObject>())
620 return true;
621 break;
622 case QMetaType::QDate:
623 case QMetaType::QTime:
624 case QMetaType::QDateTime:
625 if (var.as<DateObject>())
626 return true;
627 break;
628 default:
629 break;
630 }
631
632 return false;
633 };
634
635 // We want "foo as valuetype" to return undefined if it doesn't match.
636 return canCastValueType() ? Encode(true) : Encode::undefined();
637}
638
639ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
640{
641 // Keep this code in sync with ::virtualGet
642 PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
643 if (!id.isString())
644 return Object::virtualResolveLookupGetter(object, engine, lookup);
645 Scope scope(engine);
646
647 const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object);
648 ScopedString name(scope, id.asStringOrSymbol());
649 QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext();
650
651 Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This));
652 QQmlType type = w->d()->type();
653
654 if (type.isValid()) {
655
656 if (type.isSingleton()) {
657 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
658 if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
659 if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
660 const bool includeEnums
661 = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums;
662 if (!includeEnums || !name->startsWithUpper()) {
663 QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
664 if (ddata && ddata->propertyCache) {
665 const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
666 if (property) {
667 ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
668 if (qualifiesForMethodLookup(property)) {
669 QV4::Heap::QObjectMethod *method = nullptr;
670 setupQObjectMethodLookup(
671 lookup, ddata->propertyCache, property,
672 val->objectValue(), method);
673 lookup->call = QV4::Lookup::Call::GetterSingletonMethod;
674 } else {
675 setupQObjectLookup(
676 lookup, ddata, property, val->objectValue(), This);
677 lookup->call = QV4::Lookup::Call::GetterSingletonProperty;
678 }
679 return lookup->getter(engine, *object);
680 }
681 // Fall through to base implementation
682 }
683 // Fall through to base implementation
684 }
685 // Fall through to base implementation
686 }
687 // Fall through to base implementation
688 }
689 // Fall through to base implementation
690 }
691
692 if (name->startsWithUpper()) {
693 bool ok = false;
694 QQmlTypeLoader *typeLoader = engine->typeLoader();
695 int value = type.enumValue(typeLoader, name, &ok);
696 if (ok) {
697 lookup->qmlEnumValueLookup.ic.set(engine, This->internalClass());
698 lookup->qmlEnumValueLookup.encodedEnumValue
699 = QV4::Value::fromInt32(value).asReturnedValue();
700 lookup->call = QV4::Lookup::Call::GetterEnumValue;
701 return lookup->getter(engine, *object);
702 }
703
704 value = type.scopedEnumIndex(typeLoader, name, &ok);
705 if (ok) {
706 Scoped<QQmlEnumWrapper> enumWrapper(
707 scope, createEnumWrapper(engine, scope, type, value, true));
708 auto *wrapper = enumWrapper->as<QQmlEnumWrapper>();
709 lookup->qmlEnumWrapperLookup.ic.set(engine, This->internalClass());
710 lookup->qmlEnumWrapperLookup.qmlEnumWrapper.set(engine, wrapper->heapObject());
711 lookup->call = QV4::Lookup::Call::GetterEnum;
712 return enumWrapper.asReturnedValue();
713 }
714
715 value = type.unscopedEnumIndex(typeLoader, name, &ok);
716 if (ok) {
717 Scoped<QQmlEnumWrapper> enumWrapper(
718 scope, createEnumWrapper(engine, scope, type, value, false));
719 auto *wrapper = enumWrapper->as<QQmlEnumWrapper>();
720 lookup->qmlEnumWrapperLookup.ic.set(engine, This->internalClass());
721 lookup->qmlEnumWrapperLookup.qmlEnumWrapper.set(engine, wrapper->heapObject());
722 lookup->call = QV4::Lookup::Call::GetterEnum;
723 return enumWrapper.asReturnedValue();
724 }
725
726 // Fall through to base implementation
727 } else if (w->d()->object) {
728 QObject *ao = qmlAttachedPropertiesObject(
729 w->d()->object, type.attachedPropertiesFunction(engine->typeLoader()));
730 if (ao) {
731 // ### QTBUG-126877: Optimize this case
732 lookup->call = QV4::Lookup::Call::GetterQObjectPropertyFallback;
733 return lookup->getter(engine, *object);
734 }
735 }
736 // Fall through to base implementation
737 }
738 /* ### QTBUG-126877: use QV4::Object::virtualResolveLookupGetter once we can be sure
739 that we don't run into issues related to Function being our prototype */
740 lookup->call = QV4::Lookup::Call::GetterQObjectPropertyFallback;
741 return lookup->getter(engine, *object);
742}
743
744bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
745{
746 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
747}
748
749OwnPropertyKeyIterator *QQmlTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
750{
751 QV4::Scope scope(m->engine());
752 QV4::Scoped<QQmlTypeWrapper> typeWrapper(scope, m);
753 Q_ASSERT(typeWrapper);
754 if (QObject *object = typeWrapper->object()) {
755 QV4::Scoped<QV4::QObjectWrapper> objectWrapper(scope, QV4::QObjectWrapper::wrap(typeWrapper->engine(), object));
756 return QV4::QObjectWrapper::virtualOwnPropertyKeys(objectWrapper, target);
757 }
758
759 return Object::virtualOwnPropertyKeys(m, target);
760}
761
762int QQmlTypeWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
763{
764 QQmlTypeWrapper *wrapper = object->as<QQmlTypeWrapper>();
765 Q_ASSERT(wrapper);
766
767 if (QObject *qObject = wrapper->object())
768 return QMetaObject::metacall(qObject, call, index, a);
769
770 return 0;
771}
772
773ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object)
774{
775 const auto revertLookup = [l, engine, &object]() {
776 l->qobjectLookup.propertyCache->release();
777 l->qobjectLookup.propertyCache = nullptr;
778 l->call = QV4::Lookup::Call::GetterGeneric;
779 return Lookup::getterGeneric(l, engine, object);
780 };
781
782 // we can safely cast to a QV4::Object here. If object is something else,
783 // the internal class won't match
784 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
785
786 // The qmlTypeIc check is not strictly necessary.
787 // If we have different ways to get to the same QObject type
788 // we can use the same lookup to get its properties, no matter
789 // how we've found the object. Most of the few times this check
790 // fails, we will, of course have different object types. So
791 // this check provides an early exit for the error case.
792 //
793 // So, if we ever need more bits in qobjectLookup, qmlTypeIc is the
794 // member to be replaced.
795 if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc)
796 return revertLookup();
797
798 Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o);
799
800 QQmlType type = This->type();
801 if (!type.isValid())
802 return revertLookup();
803
804 if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
805 return revertLookup();
806
807 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
808 QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
809 Q_ASSERT(qobjectSingleton);
810
811 Scope scope(engine);
812 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
813 const QObjectWrapper::Flags flags = l->forCall
814 ? QObjectWrapper::AllowOverride
815 : (QObjectWrapper::AttachMethods | QObjectWrapper::AllowOverride);
816 return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, revertLookup);
817}
818
819ReturnedValue QQmlTypeWrapper::lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &object)
820{
821 const auto revertLookup = [l, engine, &object]() {
822 l->qobjectMethodLookup.propertyCache->release();
823 l->qobjectMethodLookup.propertyCache = nullptr;
824 l->call = QV4::Lookup::Call::GetterGeneric;
825 return Lookup::getterGeneric(l, engine, object);
826 };
827
828 // We cannot safely cast here as we don't explicitly check the IC. Therefore as().
829 const QQmlTypeWrapper *This = object.as<QQmlTypeWrapper>();
830 if (!This)
831 return revertLookup();
832
833 QQmlType type = This->d()->type();
834 if (!type.isValid())
835 return revertLookup();
836
837 if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
838 return revertLookup();
839
840 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
841 QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
842 Q_ASSERT(qobjectSingleton);
843
844 Scope scope(engine);
845 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
846 return QObjectWrapper::lookupMethodGetterImpl(
847 l, engine, obj, l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods,
848 revertLookup);
849}
850
851ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base)
852{
853 auto *o = static_cast<Heap::Object *>(base.heapObject());
854 if (!o || o->internalClass != l->qmlEnumValueLookup.ic) {
855 l->call = QV4::Lookup::Call::GetterGeneric;
856 return Lookup::getterGeneric(l, engine, base);
857 }
858
859 return l->qmlEnumValueLookup.encodedEnumValue;
860}
861
862ReturnedValue QQmlTypeWrapper::lookupEnum(Lookup *l, ExecutionEngine *engine, const Value &base)
863{
864 Scope scope(engine);
865 Scoped<QQmlEnumWrapper> enumWrapper(scope, l->qmlEnumWrapperLookup.qmlEnumWrapper.get());
866
867 auto *o = static_cast<Heap::Object *>(base.heapObject());
868 if (!o || o->internalClass != l->qmlEnumWrapperLookup.ic) {
869 QQmlType::derefHandle(enumWrapper->d()->typePrivate);
870 l->qmlEnumWrapperLookup.qmlEnumWrapper.clear();
871 l->call = QV4::Lookup::Call::GetterGeneric;
872 return Lookup::getterGeneric(l, engine, base);
873 }
874
875 return enumWrapper.asReturnedValue();
876}
877
878void Heap::QQmlEnumWrapper::destroy()
879{
880 QQmlType::derefHandle(typePrivate);
881 typePrivate = nullptr;
882 Object::destroy();
883}
884
885QQmlType Heap::QQmlEnumWrapper::type() const
886{
887 return QQmlType(typePrivate);
888}
889
890ReturnedValue QQmlEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver,
891 bool *hasProperty)
892{
893 Q_ASSERT(m->as<QQmlEnumWrapper>());
894 if (!id.isString())
895 return Object::virtualGet(m, id, receiver, hasProperty);
896
897 const QQmlEnumWrapper *resource = static_cast<const QQmlEnumWrapper *>(m);
898 QV4::ExecutionEngine *v4 = resource->engine();
899 QV4::Scope scope(v4);
900 ScopedString name(scope, id.asStringOrSymbol());
901
902 QQmlType type = resource->d()->type();
903 int index = resource->d()->enumIndex;
904
905 bool ok = false;
906 auto *typeLoader = v4->typeLoader();
907 int value = resource->d()->scoped ? type.scopedEnumValue(typeLoader, index, name, &ok)
908 : type.unscopedEnumValue(typeLoader, index, name, &ok);
909 if (hasProperty)
910 *hasProperty = ok;
911 if (ok)
912 return QV4::Value::fromInt32(value).asReturnedValue();
913
914 return Object::virtualGet(m, id, receiver, hasProperty);
915}
916
917QT_END_NAMESPACE
Combined button and popup list for selecting options.
static bool instanceHasCompilationUnit(QObject *wrapperObject)
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)