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