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 | |
4 | #include "qqmltypewrapper_p.h" |
5 | |
6 | #include <private/qqmlengine_p.h> |
7 | #include <private/qqmlcontext_p.h> |
8 | #include <private/qqmlmetaobject_p.h> |
9 | #include <private/qqmltypedata_p.h> |
10 | #include <private/qqmlvaluetypewrapper_p.h> |
11 | |
12 | #include <private/qjsvalue_p.h> |
13 | #include <private/qv4functionobject_p.h> |
14 | #include <private/qv4objectproto_p.h> |
15 | #include <private/qv4qobjectwrapper_p.h> |
16 | #include <private/qv4identifiertable_p.h> |
17 | #include <private/qv4lookup_p.h> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | using namespace QV4; |
22 | |
23 | DEFINE_OBJECT_VTABLE(QQmlTypeWrapper); |
24 | DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper); |
25 | |
26 | void Heap::QQmlTypeWrapper::init() |
27 | { |
28 | Object::init(); |
29 | mode = IncludeEnums; |
30 | object.init(); |
31 | } |
32 | |
33 | void Heap::QQmlTypeWrapper::destroy() |
34 | { |
35 | QQmlType::derefHandle(priv: typePrivate); |
36 | typePrivate = nullptr; |
37 | if (typeNamespace) |
38 | typeNamespace->release(); |
39 | object.destroy(); |
40 | Object::destroy(); |
41 | } |
42 | |
43 | QQmlType Heap::QQmlTypeWrapper::type() const |
44 | { |
45 | return QQmlType(typePrivate); |
46 | } |
47 | |
48 | bool QQmlTypeWrapper::isSingleton() const |
49 | { |
50 | return d()->type().isSingleton(); |
51 | } |
52 | |
53 | const QMetaObject *QQmlTypeWrapper::metaObject() const |
54 | { |
55 | const QQmlType type = d()->type(); |
56 | if (!type.isValid()) |
57 | return nullptr; |
58 | |
59 | if (type.isSingleton()) |
60 | return type.metaObject(); |
61 | |
62 | return type.attachedPropertiesType(engine: QQmlEnginePrivate::get(e: engine()->qmlEngine())); |
63 | } |
64 | |
65 | QObject *QQmlTypeWrapper::object() const |
66 | { |
67 | const QQmlType type = d()->type(); |
68 | if (!type.isValid()) |
69 | return nullptr; |
70 | |
71 | QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(e: engine()->qmlEngine()); |
72 | if (type.isSingleton()) |
73 | return qmlEngine->singletonInstance<QObject *>(type); |
74 | |
75 | return qmlAttachedPropertiesObject( |
76 | d()->object, |
77 | func: type.attachedPropertiesFunction(engine: qmlEngine)); |
78 | } |
79 | |
80 | QObject* QQmlTypeWrapper::singletonObject() const |
81 | { |
82 | if (!isSingleton()) |
83 | return nullptr; |
84 | |
85 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine()->qmlEngine()); |
86 | return e->singletonInstance<QObject*>(type: d()->type()); |
87 | } |
88 | |
89 | QVariant QQmlTypeWrapper::toVariant() const |
90 | { |
91 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine()->qmlEngine()); |
92 | const QQmlType type = d()->type(); |
93 | |
94 | if (!isSingleton()) { |
95 | return QVariant::fromValue(value: qmlAttachedPropertiesObject( |
96 | d()->object, func: type.attachedPropertiesFunction(engine: e))); |
97 | } |
98 | |
99 | if (type.isQJSValueSingleton()) |
100 | return QVariant::fromValue<QJSValue>(value: e->singletonInstance<QJSValue>(type)); |
101 | |
102 | return QVariant::fromValue<QObject*>(value: e->singletonInstance<QObject*>(type)); |
103 | } |
104 | |
105 | |
106 | // Returns a type wrapper for type t on o. This allows access of enums, and attached properties. |
107 | ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t, |
108 | Heap::QQmlTypeWrapper::TypeNameMode mode) |
109 | { |
110 | Q_ASSERT(t.isValid()); |
111 | Scope scope(engine); |
112 | |
113 | Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); |
114 | w->d()->mode = mode; w->d()->object = o; |
115 | w->d()->typePrivate = t.priv(); |
116 | QQmlType::refHandle(priv: w->d()->typePrivate); |
117 | return w.asReturnedValue(); |
118 | } |
119 | |
120 | // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a |
121 | // namespace. |
122 | ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace, |
123 | Heap::QQmlTypeWrapper::TypeNameMode mode) |
124 | { |
125 | Q_ASSERT(t); |
126 | Q_ASSERT(importNamespace); |
127 | Scope scope(engine); |
128 | |
129 | Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); |
130 | w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace; |
131 | t->addref(); |
132 | return w.asReturnedValue(); |
133 | } |
134 | |
135 | static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton, |
136 | const QQmlType &type, bool *ok) |
137 | { |
138 | Q_ASSERT(ok != nullptr); |
139 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok); |
140 | if (*ok) |
141 | return value; |
142 | |
143 | // ### Optimize |
144 | QByteArray enumName = name->toQString().toUtf8(); |
145 | const QMetaObject *metaObject = qobjectSingleton->metaObject(); |
146 | for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { |
147 | QMetaEnum e = metaObject->enumerator(index: ii); |
148 | value = e.keyToValue(key: enumName.constData(), ok); |
149 | if (*ok) |
150 | return value; |
151 | } |
152 | *ok = false; |
153 | return -1; |
154 | } |
155 | |
156 | ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
157 | { |
158 | // Keep this code in sync with ::virtualResolveLookupGetter |
159 | Q_ASSERT(m->as<QQmlTypeWrapper>()); |
160 | |
161 | if (!id.isString()) |
162 | return Object::virtualGet(m, id, receiver, hasProperty); |
163 | |
164 | QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine(); |
165 | QV4::Scope scope(v4); |
166 | ScopedString name(scope, id.asStringOrSymbol()); |
167 | |
168 | Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m)); |
169 | |
170 | if (hasProperty) |
171 | *hasProperty = true; |
172 | |
173 | QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); |
174 | |
175 | QObject *object = w->d()->object; |
176 | QQmlType type = w->d()->type(); |
177 | |
178 | if (type.isValid()) { |
179 | |
180 | // singleton types are handled differently to other types. |
181 | if (type.isSingleton()) { |
182 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: v4->qmlEngine()); |
183 | QJSValue scriptSingleton; |
184 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
185 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { |
186 | // check for enum value |
187 | const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; |
188 | if (includeEnums && name->startsWithUpper()) { |
189 | bool ok = false; |
190 | int value = enumForSingleton(v4, name, qobjectSingleton, type, ok: &ok); |
191 | if (ok) |
192 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
193 | |
194 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
195 | if (ok) { |
196 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
197 | enumWrapper->d()->typePrivate = type.priv(); |
198 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
199 | enumWrapper->d()->scopeEnumIndex = value; |
200 | return enumWrapper.asReturnedValue(); |
201 | } |
202 | } |
203 | |
204 | // check for property. |
205 | bool ok; |
206 | const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty( |
207 | engine: v4, qmlContext: context, wrapper: w->d(), object: qobjectSingleton, name, |
208 | flags: QV4::QObjectWrapper::AttachMethods, hasProperty: &ok); |
209 | if (hasProperty) |
210 | *hasProperty = ok; |
211 | |
212 | return result; |
213 | } |
214 | } else if (type.isQJSValueSingleton()) { |
215 | QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); |
216 | if (!scriptSingleton.isUndefined()) { |
217 | // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. |
218 | QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(jsval: &scriptSingleton)); |
219 | if (!!o) |
220 | return o->get(name); |
221 | } |
222 | } |
223 | |
224 | // Fall through to base implementation |
225 | |
226 | } else { |
227 | |
228 | if (name->startsWithUpper()) { |
229 | bool ok = false; |
230 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
231 | if (ok) |
232 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
233 | |
234 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
235 | if (ok) { |
236 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
237 | enumWrapper->d()->typePrivate = type.priv(); |
238 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
239 | enumWrapper->d()->scopeEnumIndex = value; |
240 | return enumWrapper.asReturnedValue(); |
241 | } |
242 | |
243 | // Fall through to base implementation |
244 | |
245 | } else if (w->d()->object) { |
246 | QObject *ao = qmlAttachedPropertiesObject( |
247 | object, |
248 | func: type.attachedPropertiesFunction(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()))); |
249 | if (ao) |
250 | return QV4::QObjectWrapper::getQmlProperty( |
251 | engine: v4, qmlContext: context, wrapper: w->d(), object: ao, name, flags: QV4::QObjectWrapper::AttachMethods, |
252 | hasProperty); |
253 | |
254 | // Fall through to base implementation |
255 | } |
256 | |
257 | // Fall through to base implementation |
258 | } |
259 | |
260 | // Fall through to base implementation |
261 | |
262 | } else if (w->d()->typeNamespace) { |
263 | Q_ASSERT(w->d()->importNamespace); |
264 | QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(key: name, importNamespace: w->d()->importNamespace); |
265 | |
266 | if (r.isValid()) { |
267 | if (r.type.isValid()) { |
268 | return create(engine: scope.engine, o: object, t: r.type, mode: w->d()->mode); |
269 | } else if (r.scriptIndex != -1) { |
270 | QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); |
271 | return scripts->get(idx: r.scriptIndex); |
272 | } else if (r.importNamespace) { |
273 | return create(engine: scope.engine, o: object, t: context->imports(), importNamespace: r.importNamespace); |
274 | } |
275 | |
276 | return QV4::Encode::undefined(); |
277 | |
278 | } |
279 | |
280 | // Fall through to base implementation |
281 | |
282 | } else { |
283 | Q_ASSERT(!"Unreachable" ); |
284 | } |
285 | |
286 | bool ok = false; |
287 | const ReturnedValue result = Object::virtualGet(m, id, receiver, hasProperty: &ok); |
288 | if (hasProperty) |
289 | *hasProperty = ok; |
290 | |
291 | return result; |
292 | } |
293 | |
294 | |
295 | bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
296 | { |
297 | if (!id.isString()) |
298 | return Object::virtualPut(m, id, value, receiver); |
299 | |
300 | |
301 | Q_ASSERT(m->as<QQmlTypeWrapper>()); |
302 | QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m); |
303 | QV4::Scope scope(w); |
304 | if (scope.hasException()) |
305 | return false; |
306 | |
307 | ScopedString name(scope, id.asStringOrSymbol()); |
308 | QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext(); |
309 | |
310 | QQmlType type = w->d()->type(); |
311 | if (type.isValid() && !type.isSingleton() && w->d()->object) { |
312 | QObject *object = w->d()->object; |
313 | QQmlEngine *e = scope.engine->qmlEngine(); |
314 | QObject *ao = qmlAttachedPropertiesObject( |
315 | object, func: type.attachedPropertiesFunction(engine: QQmlEnginePrivate::get(e))); |
316 | if (ao) |
317 | return QV4::QObjectWrapper::setQmlProperty( |
318 | engine: scope.engine, qmlContext: context, object: ao, name, flags: QV4::QObjectWrapper::NoFlag, value); |
319 | return false; |
320 | } else if (type.isSingleton()) { |
321 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: scope.engine->qmlEngine()); |
322 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
323 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) |
324 | return QV4::QObjectWrapper::setQmlProperty( |
325 | engine: scope.engine, qmlContext: context, object: qobjectSingleton, name, |
326 | flags: QV4::QObjectWrapper::NoFlag, value); |
327 | |
328 | } else { |
329 | QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); |
330 | if (!scriptSingleton.isUndefined()) { |
331 | QV4::ScopedObject apiprivate(scope, QJSValuePrivate::asReturnedValue(jsval: &scriptSingleton)); |
332 | if (!apiprivate) { |
333 | QString error = QLatin1String("Cannot assign to read-only property \"" ) + name->toQString() + QLatin1Char('\"'); |
334 | scope.engine->throwError(message: error); |
335 | return false; |
336 | } else { |
337 | return apiprivate->put(name, v: value); |
338 | } |
339 | } |
340 | } |
341 | } |
342 | |
343 | return false; |
344 | } |
345 | |
346 | PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
347 | { |
348 | if (id.isString()) { |
349 | Scope scope(m); |
350 | ScopedString n(scope, id.asStringOrSymbol()); |
351 | // ### Implement more efficiently. |
352 | bool hasProperty = false; |
353 | static_cast<const Object *>(m)->get(name: n, hasProperty: &hasProperty); |
354 | return hasProperty ? Attr_Data : Attr_Invalid; |
355 | } |
356 | |
357 | return QV4::Object::virtualGetOwnProperty(m, id, p); |
358 | } |
359 | |
360 | bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b) |
361 | { |
362 | Q_ASSERT(a->as<QV4::QQmlTypeWrapper>()); |
363 | QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a); |
364 | if (QV4::QQmlTypeWrapper *qmlTypeWrapperB = b->as<QV4::QQmlTypeWrapper>()) |
365 | return qmlTypeWrapperA->toVariant() == qmlTypeWrapperB->toVariant(); |
366 | else if (QV4::QObjectWrapper *qobjectWrapper = b->as<QV4::QObjectWrapper>()) |
367 | return qmlTypeWrapperA->toVariant().value<QObject*>() == qobjectWrapper->object(); |
368 | |
369 | return false; |
370 | } |
371 | |
372 | static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, const QObjectWrapper *objectWrapper) |
373 | { |
374 | QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine; |
375 | // in case the wrapper outlived the QObject* |
376 | const QObject *wrapperObject = objectWrapper->object(); |
377 | if (!wrapperObject) |
378 | return engine->throwTypeError(); |
379 | |
380 | const QMetaType myTypeId = typeWrapper->d()->type().typeId(); |
381 | QQmlMetaObject myQmlType; |
382 | if (!myTypeId.isValid()) { |
383 | // we're a composite type; a composite type cannot be equal to a |
384 | // non-composite object instance (Rectangle{} is never an instance of |
385 | // CustomRectangle) |
386 | QQmlData *theirDData = QQmlData::get(object: wrapperObject); |
387 | Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?! |
388 | if (!theirDData->compilationUnit) |
389 | return Encode(false); |
390 | |
391 | QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
392 | QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(unNormalizedUrl: typeWrapper->d()->type().sourceUrl()); |
393 | if (ExecutableCompilationUnit *cu = td->compilationUnit()) |
394 | myQmlType = QQmlMetaType::metaObjectForType(metaType: cu->typeIds.id); |
395 | else |
396 | return Encode(false); // It seems myQmlType has some errors, so we could not compile it. |
397 | } else { |
398 | myQmlType = QQmlMetaType::metaObjectForType(metaType: myTypeId); |
399 | } |
400 | |
401 | const QMetaObject *theirType = wrapperObject->metaObject(); |
402 | |
403 | return QV4::Encode(QQmlMetaObject::canConvert(from: theirType, to: myQmlType)); |
404 | } |
405 | |
406 | ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) |
407 | { |
408 | Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>()); |
409 | const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); |
410 | |
411 | if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>()) |
412 | return instanceOfQObject(typeWrapper, objectWrapper); |
413 | |
414 | if (const QMetaObject *valueTypeMetaObject |
415 | = QQmlMetaType::metaObjectForValueType(qmlType: typeWrapper->d()->type())) { |
416 | if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) { |
417 | return QV4::Encode(QQmlMetaObject::canConvert(from: valueWrapper->metaObject(), |
418 | to: valueTypeMetaObject)); |
419 | } |
420 | |
421 | // We want "foo as valuetype" to return undefined if it doesn't match. |
422 | return Encode::undefined(); |
423 | } |
424 | |
425 | // If the target type is an object type we want null. |
426 | return Encode(false); |
427 | } |
428 | |
429 | ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) |
430 | { |
431 | // Keep this code in sync with ::virtualGet |
432 | PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); |
433 | if (!id.isString()) |
434 | return Object::virtualResolveLookupGetter(object, engine, lookup); |
435 | Scope scope(engine); |
436 | |
437 | const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object); |
438 | ScopedString name(scope, id.asStringOrSymbol()); |
439 | QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext(); |
440 | |
441 | Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This)); |
442 | QQmlType type = w->d()->type(); |
443 | |
444 | if (type.isValid()) { |
445 | |
446 | if (type.isSingleton()) { |
447 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
448 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
449 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { |
450 | const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; |
451 | if (!includeEnums || !name->startsWithUpper()) { |
452 | QQmlData *ddata = QQmlData::get(object: qobjectSingleton, create: false); |
453 | if (ddata && ddata->propertyCache) { |
454 | const QQmlPropertyData *property = ddata->propertyCache->property(key: name.getPointer(), object: qobjectSingleton, context: qmlContext); |
455 | if (property) { |
456 | ScopedValue val(scope, Value::fromReturnedValue(val: QV4::QObjectWrapper::wrap(engine, object: qobjectSingleton))); |
457 | if (qualifiesForMethodLookup(propertyData: property)) { |
458 | setupQObjectMethodLookup( |
459 | lookup, ddata, propertyData: property, self: val->objectValue(), method: nullptr); |
460 | lookup->getter = QQmlTypeWrapper::lookupSingletonMethod; |
461 | } else { |
462 | setupQObjectLookup( |
463 | lookup, ddata, propertyData: property, self: val->objectValue(), qmlType: This); |
464 | lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; |
465 | } |
466 | return lookup->getter(lookup, engine, *object); |
467 | } |
468 | // Fall through to base implementation |
469 | } |
470 | // Fall through to base implementation |
471 | } |
472 | // Fall through to base implementation |
473 | } |
474 | // Fall through to base implementation |
475 | } |
476 | // Fall through to base implementation |
477 | } |
478 | |
479 | if (name->startsWithUpper()) { |
480 | bool ok = false; |
481 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: engine->qmlEngine()), name, ok: &ok); |
482 | if (ok) { |
483 | lookup->qmlEnumValueLookup.ic = This->internalClass(); |
484 | lookup->qmlEnumValueLookup.encodedEnumValue |
485 | = QV4::Value::fromInt32(i: value).asReturnedValue(); |
486 | lookup->getter = QQmlTypeWrapper::lookupEnumValue; |
487 | return lookup->getter(lookup, engine, *object); |
488 | } |
489 | |
490 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: engine->qmlEngine()), name, ok: &ok); |
491 | if (ok) { |
492 | Scoped<QQmlScopedEnumWrapper> enumWrapper( |
493 | scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
494 | enumWrapper->d()->typePrivate = type.priv(); |
495 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
496 | enumWrapper->d()->scopeEnumIndex = value; |
497 | |
498 | lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); |
499 | lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper |
500 | = static_cast<Heap::Object*>(enumWrapper->heapObject()); |
501 | lookup->getter = QQmlTypeWrapper::lookupScopedEnum; |
502 | return enumWrapper.asReturnedValue(); |
503 | } |
504 | // Fall through to base implementation |
505 | } |
506 | // Fall through to base implementation |
507 | } |
508 | return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); |
509 | } |
510 | |
511 | bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) |
512 | { |
513 | return Object::virtualResolveLookupSetter(object, engine, lookup, value); |
514 | } |
515 | |
516 | OwnPropertyKeyIterator *QQmlTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) |
517 | { |
518 | QV4::Scope scope(m->engine()); |
519 | QV4::Scoped<QQmlTypeWrapper> typeWrapper(scope, m); |
520 | Q_ASSERT(typeWrapper); |
521 | if (QObject *object = typeWrapper->object()) { |
522 | QV4::Scoped<QV4::QObjectWrapper> objectWrapper(scope, QV4::QObjectWrapper::wrap(engine: typeWrapper->engine(), object)); |
523 | return QV4::QObjectWrapper::virtualOwnPropertyKeys(m: objectWrapper, target); |
524 | } |
525 | |
526 | return Object::virtualOwnPropertyKeys(m, target); |
527 | } |
528 | |
529 | int QQmlTypeWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) |
530 | { |
531 | QQmlTypeWrapper *wrapper = object->as<QQmlTypeWrapper>(); |
532 | Q_ASSERT(wrapper); |
533 | |
534 | if (QObject *qObject = wrapper->object()) |
535 | return QMetaObject::metacall(qObject, call, index, a); |
536 | |
537 | return 0; |
538 | } |
539 | |
540 | ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object) |
541 | { |
542 | const auto revertLookup = [l, engine, &object]() { |
543 | l->qobjectLookup.propertyCache->release(); |
544 | l->qobjectLookup.propertyCache = nullptr; |
545 | l->getter = Lookup::getterGeneric; |
546 | return Lookup::getterGeneric(l, engine, object); |
547 | }; |
548 | |
549 | // we can safely cast to a QV4::Object here. If object is something else, |
550 | // the internal class won't match |
551 | Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); |
552 | |
553 | // The qmlTypeIc check is not strictly necessary. |
554 | // If we have different ways to get to the same QObject type |
555 | // we can use the same lookup to get its properties, no matter |
556 | // how we've found the object. Most of the few times this check |
557 | // fails, we will, of course have different object types. So |
558 | // this check provides an early exit for the error case. |
559 | // |
560 | // So, if we ever need more bits in qobjectLookup, qmlTypeIc is the |
561 | // member to be replaced. |
562 | if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc) |
563 | return revertLookup(); |
564 | |
565 | Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o); |
566 | |
567 | QQmlType type = This->type(); |
568 | if (!type.isValid()) |
569 | return revertLookup(); |
570 | |
571 | if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) |
572 | return revertLookup(); |
573 | |
574 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
575 | QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); |
576 | Q_ASSERT(qobjectSingleton); |
577 | |
578 | Scope scope(engine); |
579 | ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: qobjectSingleton)); |
580 | const QObjectWrapper::Flags flags = l->forCall |
581 | ? QObjectWrapper::AllowOverride |
582 | : (QObjectWrapper::AttachMethods | QObjectWrapper::AllowOverride); |
583 | return QObjectWrapper::lookupPropertyGetterImpl(lookup: l, engine, object: obj, flags, revertLookup); |
584 | } |
585 | |
586 | ReturnedValue QQmlTypeWrapper::lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &object) |
587 | { |
588 | const auto revertLookup = [l, engine, &object]() { |
589 | l->qobjectMethodLookup.propertyCache->release(); |
590 | l->qobjectMethodLookup.propertyCache = nullptr; |
591 | l->getter = Lookup::getterGeneric; |
592 | return Lookup::getterGeneric(l, engine, object); |
593 | }; |
594 | |
595 | // We cannot safely cast here as we don't explicitly check the IC. Therefore as(). |
596 | const QQmlTypeWrapper *This = object.as<QQmlTypeWrapper>(); |
597 | if (!This) |
598 | return revertLookup(); |
599 | |
600 | QQmlType type = This->d()->type(); |
601 | if (!type.isValid()) |
602 | return revertLookup(); |
603 | |
604 | if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) |
605 | return revertLookup(); |
606 | |
607 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
608 | QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); |
609 | Q_ASSERT(qobjectSingleton); |
610 | |
611 | Scope scope(engine); |
612 | ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: qobjectSingleton)); |
613 | return QObjectWrapper::lookupMethodGetterImpl( |
614 | lookup: l, engine, object: obj, flags: l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods, |
615 | revertLookup); |
616 | } |
617 | |
618 | ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) |
619 | { |
620 | auto *o = static_cast<Heap::Object *>(base.heapObject()); |
621 | if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { |
622 | l->getter = Lookup::getterGeneric; |
623 | return Lookup::getterGeneric(l, engine, object: base); |
624 | } |
625 | |
626 | return l->qmlEnumValueLookup.encodedEnumValue; |
627 | } |
628 | |
629 | ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) |
630 | { |
631 | Scope scope(engine); |
632 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( |
633 | l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); |
634 | |
635 | auto *o = static_cast<Heap::Object *>(base.heapObject()); |
636 | if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { |
637 | QQmlType::derefHandle(priv: enumWrapper->d()->typePrivate); |
638 | l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; |
639 | l->getter = Lookup::getterGeneric; |
640 | return Lookup::getterGeneric(l, engine, object: base); |
641 | } |
642 | |
643 | return enumWrapper.asReturnedValue(); |
644 | } |
645 | |
646 | void Heap::QQmlScopedEnumWrapper::destroy() |
647 | { |
648 | QQmlType::derefHandle(priv: typePrivate); |
649 | typePrivate = nullptr; |
650 | Object::destroy(); |
651 | } |
652 | |
653 | QQmlType Heap::QQmlScopedEnumWrapper::type() const |
654 | { |
655 | return QQmlType(typePrivate); |
656 | } |
657 | |
658 | ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
659 | { |
660 | Q_ASSERT(m->as<QQmlScopedEnumWrapper>()); |
661 | if (!id.isString()) |
662 | return Object::virtualGet(m, id, receiver, hasProperty); |
663 | |
664 | const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m); |
665 | QV4::ExecutionEngine *v4 = resource->engine(); |
666 | QV4::Scope scope(v4); |
667 | ScopedString name(scope, id.asStringOrSymbol()); |
668 | |
669 | QQmlType type = resource->d()->type(); |
670 | int index = resource->d()->scopeEnumIndex; |
671 | |
672 | bool ok = false; |
673 | int value = type.scopedEnumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), index, name, ok: &ok); |
674 | if (hasProperty) |
675 | *hasProperty = ok; |
676 | if (ok) |
677 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
678 | |
679 | return Encode::undefined(); |
680 | } |
681 | |
682 | QT_END_NAMESPACE |
683 | |