1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmltypewrapper_p.h" |
41 | |
42 | #include <private/qqmlengine_p.h> |
43 | #include <private/qqmlcontext_p.h> |
44 | #include <private/qqmlmetaobject_p.h> |
45 | #include <private/qqmltypedata_p.h> |
46 | |
47 | #include <private/qjsvalue_p.h> |
48 | #include <private/qv4functionobject_p.h> |
49 | #include <private/qv4objectproto_p.h> |
50 | #include <private/qv4qobjectwrapper_p.h> |
51 | #include <private/qv4identifiertable_p.h> |
52 | #include <private/qv4lookup_p.h> |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | using namespace QV4; |
57 | |
58 | DEFINE_OBJECT_VTABLE(QQmlTypeWrapper); |
59 | DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper); |
60 | |
61 | void Heap::QQmlTypeWrapper::init() |
62 | { |
63 | Object::init(); |
64 | mode = IncludeEnums; |
65 | object.init(); |
66 | } |
67 | |
68 | void Heap::QQmlTypeWrapper::destroy() |
69 | { |
70 | QQmlType::derefHandle(priv: typePrivate); |
71 | typePrivate = nullptr; |
72 | if (typeNamespace) |
73 | typeNamespace->release(); |
74 | object.destroy(); |
75 | Object::destroy(); |
76 | } |
77 | |
78 | QQmlType Heap::QQmlTypeWrapper::type() const |
79 | { |
80 | return QQmlType(typePrivate); |
81 | } |
82 | |
83 | bool QQmlTypeWrapper::isSingleton() const |
84 | { |
85 | return d()->type().isSingleton(); |
86 | } |
87 | |
88 | QObject* QQmlTypeWrapper::singletonObject() const |
89 | { |
90 | if (!isSingleton()) |
91 | return nullptr; |
92 | |
93 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine()->qmlEngine()); |
94 | return e->singletonInstance<QObject*>(type: d()->type()); |
95 | } |
96 | |
97 | QVariant QQmlTypeWrapper::toVariant() const |
98 | { |
99 | if (!isSingleton()) |
100 | return QVariant::fromValue<QObject *>(value: d()->object); |
101 | |
102 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine()->qmlEngine()); |
103 | const QQmlType type = d()->type(); |
104 | if (type.isQJSValueSingleton()) |
105 | return QVariant::fromValue<QJSValue>(value: e->singletonInstance<QJSValue>(type)); |
106 | |
107 | return QVariant::fromValue<QObject*>(value: e->singletonInstance<QObject*>(type)); |
108 | } |
109 | |
110 | |
111 | // Returns a type wrapper for type t on o. This allows access of enums, and attached properties. |
112 | ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t, |
113 | Heap::QQmlTypeWrapper::TypeNameMode mode) |
114 | { |
115 | Q_ASSERT(t.isValid()); |
116 | Scope scope(engine); |
117 | |
118 | Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); |
119 | w->d()->mode = mode; w->d()->object = o; |
120 | w->d()->typePrivate = t.priv(); |
121 | QQmlType::refHandle(priv: w->d()->typePrivate); |
122 | return w.asReturnedValue(); |
123 | } |
124 | |
125 | // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a |
126 | // namespace. |
127 | ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace, |
128 | Heap::QQmlTypeWrapper::TypeNameMode mode) |
129 | { |
130 | Q_ASSERT(t); |
131 | Q_ASSERT(importNamespace); |
132 | Scope scope(engine); |
133 | |
134 | Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); |
135 | w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace; |
136 | t->addref(); |
137 | return w.asReturnedValue(); |
138 | } |
139 | |
140 | static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton, |
141 | const QQmlType &type, bool *ok) |
142 | { |
143 | Q_ASSERT(ok != nullptr); |
144 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok); |
145 | if (*ok) |
146 | return value; |
147 | |
148 | // ### Optimize |
149 | QByteArray enumName = name->toQString().toUtf8(); |
150 | const QMetaObject *metaObject = qobjectSingleton->metaObject(); |
151 | for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { |
152 | QMetaEnum e = metaObject->enumerator(index: ii); |
153 | value = e.keyToValue(key: enumName.constData(), ok); |
154 | if (*ok) |
155 | return value; |
156 | } |
157 | *ok = false; |
158 | return -1; |
159 | } |
160 | |
161 | static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *name, const QQmlType &type) |
162 | { |
163 | const QString message = |
164 | QStringLiteral("Cannot access enum value '%1' of '%2', enum values need to start with an uppercase letter." ) |
165 | .arg(a: name->toQString()).arg(a: QLatin1String(type.typeName())); |
166 | return v4->throwTypeError(message); |
167 | } |
168 | |
169 | ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
170 | { |
171 | // Keep this code in sync with ::virtualResolveLookupGetter |
172 | Q_ASSERT(m->as<QQmlTypeWrapper>()); |
173 | |
174 | if (!id.isString()) |
175 | return Object::virtualGet(m, id, receiver, hasProperty); |
176 | |
177 | QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine(); |
178 | QV4::Scope scope(v4); |
179 | ScopedString name(scope, id.asStringOrSymbol()); |
180 | |
181 | Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m)); |
182 | |
183 | if (hasProperty) |
184 | *hasProperty = true; |
185 | |
186 | QQmlContextData *context = v4->callingQmlContext(); |
187 | |
188 | QObject *object = w->d()->object; |
189 | QQmlType type = w->d()->type(); |
190 | |
191 | if (type.isValid()) { |
192 | |
193 | // singleton types are handled differently to other types. |
194 | if (type.isSingleton()) { |
195 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: v4->qmlEngine()); |
196 | QJSValue scriptSingleton; |
197 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
198 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { |
199 | // check for enum value |
200 | const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; |
201 | if (includeEnums && name->startsWithUpper()) { |
202 | bool ok = false; |
203 | int value = enumForSingleton(v4, name, qobjectSingleton, type, ok: &ok); |
204 | if (ok) |
205 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
206 | |
207 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
208 | if (ok) { |
209 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
210 | enumWrapper->d()->typePrivate = type.priv(); |
211 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
212 | enumWrapper->d()->scopeEnumIndex = value; |
213 | return enumWrapper.asReturnedValue(); |
214 | } |
215 | } |
216 | |
217 | // check for property. |
218 | bool ok; |
219 | const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(engine: v4, qmlContext: context, object: qobjectSingleton, name, revisionMode: QV4::QObjectWrapper::IgnoreRevision, hasProperty: &ok); |
220 | if (hasProperty) |
221 | *hasProperty = ok; |
222 | |
223 | // Warn when attempting to access a lowercased enum value, singleton case |
224 | if (!ok && includeEnums && !name->startsWithUpper()) { |
225 | enumForSingleton(v4, name, qobjectSingleton, type, ok: &ok); |
226 | if (ok) |
227 | return throwLowercaseEnumError(v4, name, type); |
228 | } |
229 | |
230 | return result; |
231 | } |
232 | } else if (type.isQJSValueSingleton()) { |
233 | QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); |
234 | if (!scriptSingleton.isUndefined()) { |
235 | // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. |
236 | QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(e: v4, jsval: scriptSingleton)); |
237 | if (!!o) |
238 | return o->get(name); |
239 | } |
240 | } |
241 | |
242 | // Fall through to base implementation |
243 | |
244 | } else { |
245 | |
246 | if (name->startsWithUpper()) { |
247 | bool ok = false; |
248 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
249 | if (ok) |
250 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
251 | |
252 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &ok); |
253 | if (ok) { |
254 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
255 | enumWrapper->d()->typePrivate = type.priv(); |
256 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
257 | enumWrapper->d()->scopeEnumIndex = value; |
258 | return enumWrapper.asReturnedValue(); |
259 | } |
260 | |
261 | // Fall through to base implementation |
262 | |
263 | } else if (w->d()->object) { |
264 | QObject *ao = qmlAttachedPropertiesObject( |
265 | object, |
266 | func: type.attachedPropertiesFunction(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()))); |
267 | if (ao) |
268 | return QV4::QObjectWrapper::getQmlProperty(engine: v4, qmlContext: context, object: ao, name, revisionMode: QV4::QObjectWrapper::IgnoreRevision, hasProperty); |
269 | |
270 | // Fall through to base implementation |
271 | } |
272 | |
273 | // Fall through to base implementation |
274 | } |
275 | |
276 | // Fall through to base implementation |
277 | |
278 | } else if (w->d()->typeNamespace) { |
279 | Q_ASSERT(w->d()->importNamespace); |
280 | QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(name, importNamespace: w->d()->importNamespace); |
281 | |
282 | if (r.isValid()) { |
283 | if (r.type.isValid()) { |
284 | return create(engine: scope.engine, o: object, t: r.type, mode: w->d()->mode); |
285 | } else if (r.scriptIndex != -1) { |
286 | QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); |
287 | return scripts->get(idx: r.scriptIndex); |
288 | } else if (r.importNamespace) { |
289 | return create(engine: scope.engine, o: object, t: context->imports, importNamespace: r.importNamespace); |
290 | } |
291 | |
292 | return QV4::Encode::undefined(); |
293 | |
294 | } |
295 | |
296 | // Fall through to base implementation |
297 | |
298 | } else { |
299 | Q_ASSERT(!"Unreachable" ); |
300 | } |
301 | |
302 | bool ok = false; |
303 | const ReturnedValue result = Object::virtualGet(m, id, receiver, hasProperty: &ok); |
304 | if (hasProperty) |
305 | *hasProperty = ok; |
306 | |
307 | // Warn when attempting to access a lowercased enum value, non-singleton case |
308 | if (!ok && type.isValid() && !type.isSingleton() && !name->startsWithUpper()) { |
309 | bool enumOk = false; |
310 | type.enumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), name, ok: &enumOk); |
311 | if (enumOk) |
312 | return throwLowercaseEnumError(v4, name, type); |
313 | } |
314 | |
315 | return result; |
316 | } |
317 | |
318 | |
319 | bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
320 | { |
321 | if (!id.isString()) |
322 | return Object::virtualPut(m, id, value, receiver); |
323 | |
324 | |
325 | Q_ASSERT(m->as<QQmlTypeWrapper>()); |
326 | QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m); |
327 | QV4::Scope scope(w); |
328 | if (scope.engine->hasException) |
329 | return false; |
330 | |
331 | ScopedString name(scope, id.asStringOrSymbol()); |
332 | QQmlContextData *context = scope.engine->callingQmlContext(); |
333 | |
334 | QQmlType type = w->d()->type(); |
335 | if (type.isValid() && !type.isSingleton() && w->d()->object) { |
336 | QObject *object = w->d()->object; |
337 | QQmlEngine *e = scope.engine->qmlEngine(); |
338 | QObject *ao = qmlAttachedPropertiesObject( |
339 | object, func: type.attachedPropertiesFunction(engine: QQmlEnginePrivate::get(e))); |
340 | if (ao) |
341 | return QV4::QObjectWrapper::setQmlProperty(engine: scope.engine, qmlContext: context, object: ao, name, revisionMode: QV4::QObjectWrapper::IgnoreRevision, value); |
342 | return false; |
343 | } else if (type.isSingleton()) { |
344 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: scope.engine->qmlEngine()); |
345 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
346 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) |
347 | return QV4::QObjectWrapper::setQmlProperty(engine: scope.engine, qmlContext: context, object: qobjectSingleton, name, revisionMode: QV4::QObjectWrapper::IgnoreRevision, value); |
348 | |
349 | } else { |
350 | QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); |
351 | if (!scriptSingleton.isUndefined()) { |
352 | QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(e: scope.engine, jsval: scriptSingleton)); |
353 | if (!apiprivate) { |
354 | QString error = QLatin1String("Cannot assign to read-only property \"" ) + name->toQString() + QLatin1Char('\"'); |
355 | scope.engine->throwError(message: error); |
356 | return false; |
357 | } else { |
358 | return apiprivate->put(name, v: value); |
359 | } |
360 | } |
361 | } |
362 | } |
363 | |
364 | return false; |
365 | } |
366 | |
367 | PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) |
368 | { |
369 | if (id.isString()) { |
370 | Scope scope(m); |
371 | ScopedString n(scope, id.asStringOrSymbol()); |
372 | // ### Implement more efficiently. |
373 | bool hasProperty = false; |
374 | static_cast<const Object *>(m)->get(name: n, hasProperty: &hasProperty); |
375 | return hasProperty ? Attr_Data : Attr_Invalid; |
376 | } |
377 | |
378 | return QV4::Object::virtualGetOwnProperty(m, id, p); |
379 | } |
380 | |
381 | bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b) |
382 | { |
383 | Q_ASSERT(a->as<QV4::QQmlTypeWrapper>()); |
384 | QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a); |
385 | if (QV4::QQmlTypeWrapper *qmlTypeWrapperB = b->as<QV4::QQmlTypeWrapper>()) |
386 | return qmlTypeWrapperA->toVariant() == qmlTypeWrapperB->toVariant(); |
387 | else if (QV4::QObjectWrapper *qobjectWrapper = b->as<QV4::QObjectWrapper>()) |
388 | return qmlTypeWrapperA->toVariant().value<QObject*>() == qobjectWrapper->object(); |
389 | |
390 | return false; |
391 | } |
392 | |
393 | ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) |
394 | { |
395 | Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>()); |
396 | const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); |
397 | QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; |
398 | QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
399 | |
400 | // can only compare a QObject* against a QML type |
401 | const QObjectWrapper *wrapper = var.as<QObjectWrapper>(); |
402 | if (!wrapper) |
403 | return QV4::Encode(false); |
404 | |
405 | // in case the wrapper outlived the QObject* |
406 | const QObject *wrapperObject = wrapper->object(); |
407 | if (!wrapperObject) |
408 | return engine->throwTypeError(); |
409 | |
410 | const int myTypeId = typeWrapper->d()->type().typeId(); |
411 | QQmlMetaObject myQmlType; |
412 | if (myTypeId == 0) { |
413 | // we're a composite type; a composite type cannot be equal to a |
414 | // non-composite object instance (Rectangle{} is never an instance of |
415 | // CustomRectangle) |
416 | QQmlData *theirDData = QQmlData::get(object: wrapperObject, /*create=*/false); |
417 | Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?! |
418 | if (!theirDData->compilationUnit) |
419 | return Encode(false); |
420 | |
421 | QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(unNormalizedUrl: typeWrapper->d()->type().sourceUrl()); |
422 | ExecutableCompilationUnit *cu = td->compilationUnit(); |
423 | myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); |
424 | } else { |
425 | myQmlType = qenginepriv->metaObjectForType(myTypeId); |
426 | } |
427 | |
428 | const QMetaObject *theirType = wrapperObject->metaObject(); |
429 | |
430 | return QV4::Encode(QQmlMetaObject::canConvert(from: theirType, to: myQmlType)); |
431 | } |
432 | |
433 | ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) |
434 | { |
435 | // Keep this code in sync with ::virtualGet |
436 | PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); |
437 | if (!id.isString()) |
438 | return Object::virtualResolveLookupGetter(object, engine, lookup); |
439 | Scope scope(engine); |
440 | |
441 | const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object); |
442 | ScopedString name(scope, id.asStringOrSymbol()); |
443 | QQmlContextData *qmlContext = engine->callingQmlContext(); |
444 | |
445 | Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This)); |
446 | QQmlType type = w->d()->type(); |
447 | |
448 | if (type.isValid()) { |
449 | |
450 | if (type.isSingleton()) { |
451 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
452 | if (type.isQObjectSingleton() || type.isCompositeSingleton()) { |
453 | if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { |
454 | const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; |
455 | if (!includeEnums || !name->startsWithUpper()) { |
456 | QQmlData *ddata = QQmlData::get(object: qobjectSingleton, create: false); |
457 | if (ddata && ddata->propertyCache) { |
458 | QQmlPropertyData *property = ddata->propertyCache->property(key: name.getPointer(), object: qobjectSingleton, context: qmlContext); |
459 | if (property) { |
460 | ScopedValue val(scope, Value::fromReturnedValue(val: QV4::QObjectWrapper::wrap(engine, object: qobjectSingleton))); |
461 | setupQObjectLookup(lookup, ddata, propertyData: property, |
462 | self: val->objectValue(), qmlType: This); |
463 | lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; |
464 | return lookup->getter(lookup, engine, *object); |
465 | } |
466 | // Fall through to base implementation |
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 | |
477 | if (name->startsWithUpper()) { |
478 | bool ok = false; |
479 | int value = type.enumValue(engine: QQmlEnginePrivate::get(e: engine->qmlEngine()), name, ok: &ok); |
480 | if (ok) { |
481 | lookup->qmlEnumValueLookup.ic = This->internalClass(); |
482 | lookup->qmlEnumValueLookup.encodedEnumValue |
483 | = QV4::Value::fromInt32(i: value).asReturnedValue(); |
484 | lookup->getter = QQmlTypeWrapper::lookupEnumValue; |
485 | return lookup->getter(lookup, engine, *object); |
486 | } |
487 | |
488 | value = type.scopedEnumIndex(engine: QQmlEnginePrivate::get(e: engine->qmlEngine()), name, ok: &ok); |
489 | if (ok) { |
490 | Scoped<QQmlScopedEnumWrapper> enumWrapper( |
491 | scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); |
492 | enumWrapper->d()->typePrivate = type.priv(); |
493 | QQmlType::refHandle(priv: enumWrapper->d()->typePrivate); |
494 | enumWrapper->d()->scopeEnumIndex = value; |
495 | |
496 | lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); |
497 | lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper |
498 | = static_cast<Heap::Object*>(enumWrapper->heapObject()); |
499 | lookup->getter = QQmlTypeWrapper::lookupScopedEnum; |
500 | return enumWrapper.asReturnedValue(); |
501 | } |
502 | // Fall through to base implementation |
503 | } |
504 | // Fall through to base implementation |
505 | } |
506 | return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); |
507 | } |
508 | |
509 | bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) |
510 | { |
511 | return Object::virtualResolveLookupSetter(object, engine, lookup, value); |
512 | } |
513 | |
514 | ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object) |
515 | { |
516 | const auto revertLookup = [l, engine, &object]() { |
517 | l->qobjectLookup.propertyCache->release(); |
518 | l->qobjectLookup.propertyCache = nullptr; |
519 | l->getter = Lookup::getterGeneric; |
520 | return Lookup::getterGeneric(l, engine, object); |
521 | }; |
522 | |
523 | // we can safely cast to a QV4::Object here. If object is something else, |
524 | // the internal class won't match |
525 | Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); |
526 | if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc) |
527 | return revertLookup(); |
528 | |
529 | Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o); |
530 | |
531 | QQmlType type = This->type(); |
532 | if (!type.isValid()) |
533 | return revertLookup(); |
534 | |
535 | if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) |
536 | return revertLookup(); |
537 | |
538 | QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
539 | QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); |
540 | Q_ASSERT(qobjectSingleton); |
541 | |
542 | Scope scope(engine); |
543 | ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: qobjectSingleton)); |
544 | return QObjectWrapper::lookupGetterImpl(lookup: l, engine, object: obj, /*useOriginalProperty*/ true, revertLookup); |
545 | } |
546 | |
547 | ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) |
548 | { |
549 | auto *o = static_cast<Heap::Object *>(base.heapObject()); |
550 | if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { |
551 | l->getter = Lookup::getterGeneric; |
552 | return Lookup::getterGeneric(l, engine, object: base); |
553 | } |
554 | |
555 | return l->qmlEnumValueLookup.encodedEnumValue; |
556 | } |
557 | |
558 | ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) |
559 | { |
560 | Scope scope(engine); |
561 | Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( |
562 | l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); |
563 | |
564 | auto *o = static_cast<Heap::Object *>(base.heapObject()); |
565 | if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { |
566 | QQmlType::derefHandle(priv: enumWrapper->d()->typePrivate); |
567 | l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; |
568 | l->getter = Lookup::getterGeneric; |
569 | return Lookup::getterGeneric(l, engine, object: base); |
570 | } |
571 | |
572 | return enumWrapper.asReturnedValue(); |
573 | } |
574 | |
575 | void Heap::QQmlScopedEnumWrapper::destroy() |
576 | { |
577 | QQmlType::derefHandle(priv: typePrivate); |
578 | typePrivate = nullptr; |
579 | Object::destroy(); |
580 | } |
581 | |
582 | QQmlType Heap::QQmlScopedEnumWrapper::type() const |
583 | { |
584 | return QQmlType(typePrivate); |
585 | } |
586 | |
587 | ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
588 | { |
589 | Q_ASSERT(m->as<QQmlScopedEnumWrapper>()); |
590 | if (!id.isString()) |
591 | return Object::virtualGet(m, id, receiver, hasProperty); |
592 | |
593 | const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m); |
594 | QV4::ExecutionEngine *v4 = resource->engine(); |
595 | QV4::Scope scope(v4); |
596 | ScopedString name(scope, id.asStringOrSymbol()); |
597 | |
598 | QQmlType type = resource->d()->type(); |
599 | int index = resource->d()->scopeEnumIndex; |
600 | |
601 | bool ok = false; |
602 | int value = type.scopedEnumValue(engine: QQmlEnginePrivate::get(e: v4->qmlEngine()), index, name, ok: &ok); |
603 | if (hasProperty) |
604 | *hasProperty = ok; |
605 | if (ok) |
606 | return QV4::Value::fromInt32(i: value).asReturnedValue(); |
607 | |
608 | return Encode::undefined(); |
609 | } |
610 | |
611 | QT_END_NAMESPACE |
612 | |