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
54QT_BEGIN_NAMESPACE
55
56using namespace QV4;
57
58DEFINE_OBJECT_VTABLE(QQmlTypeWrapper);
59DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper);
60
61void Heap::QQmlTypeWrapper::init()
62{
63 Object::init();
64 mode = IncludeEnums;
65 object.init();
66}
67
68void Heap::QQmlTypeWrapper::destroy()
69{
70 QQmlType::derefHandle(typePrivate);
71 typePrivate = nullptr;
72 if (typeNamespace)
73 typeNamespace->release();
74 object.destroy();
75 Object::destroy();
76}
77
78QQmlType Heap::QQmlTypeWrapper::type() const
79{
80 return QQmlType(typePrivate);
81}
82
83bool QQmlTypeWrapper::isSingleton() const
84{
85 return d()->type().isSingleton();
86}
87
88QObject* QQmlTypeWrapper::singletonObject() const
89{
90 if (!isSingleton())
91 return nullptr;
92
93 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
94 return e->singletonInstance<QObject*>(d()->type());
95}
96
97QVariant QQmlTypeWrapper::toVariant() const
98{
99 if (!isSingleton())
100 return QVariant::fromValue<QObject *>(d()->object);
101
102 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
103 const QQmlType type = d()->type();
104 if (type.isQJSValueSingleton())
105 return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type));
106
107 return QVariant::fromValue<QObject*>(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.
112ReturnedValue 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(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.
127ReturnedValue 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
140static 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(QQmlEnginePrivate::get(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(ii);
153 value = e.keyToValue(enumName.constData(), ok);
154 if (*ok)
155 return value;
156 }
157 *ok = false;
158 return -1;
159}
160
161static 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(name->toQString()).arg(QLatin1String(type.typeName()));
166 return v4->throwTypeError(message);
167}
168
169ReturnedValue 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(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);
204 if (ok)
205 return QV4::Value::fromInt32(value).asReturnedValue();
206
207 value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
208 if (ok) {
209 Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
210 enumWrapper->d()->typePrivate = type.priv();
211 QQmlType::refHandle(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(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &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);
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(v4, 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(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
249 if (ok)
250 return QV4::Value::fromInt32(value).asReturnedValue();
251
252 value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
253 if (ok) {
254 Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
255 enumWrapper->d()->typePrivate = type.priv();
256 QQmlType::refHandle(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 type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine())));
267 if (ao)
268 return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, 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, w->d()->importNamespace);
281
282 if (r.isValid()) {
283 if (r.type.isValid()) {
284 return create(scope.engine, object, r.type, w->d()->mode);
285 } else if (r.scriptIndex != -1) {
286 QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
287 return scripts->get(r.scriptIndex);
288 } else if (r.importNamespace) {
289 return create(scope.engine, object, context->imports, 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, &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(QQmlEnginePrivate::get(v4->qmlEngine()), name, &enumOk);
311 if (enumOk)
312 return throwLowercaseEnumError(v4, name, type);
313 }
314
315 return result;
316}
317
318
319bool 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, type.attachedPropertiesFunction(QQmlEnginePrivate::get(e)));
340 if (ao)
341 return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
342 return false;
343 } else if (type.isSingleton()) {
344 QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine());
345 if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
346 if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type))
347 return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, 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(scope.engine, scriptSingleton));
353 if (!apiprivate) {
354 QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
355 scope.engine->throwError(error);
356 return false;
357 } else {
358 return apiprivate->put(name, value);
359 }
360 }
361 }
362 }
363
364 return false;
365}
366
367PropertyAttributes 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(n, &hasProperty);
375 return hasProperty ? Attr_Data : Attr_Invalid;
376 }
377
378 return QV4::Object::virtualGetOwnProperty(m, id, p);
379}
380
381bool 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
393ReturnedValue 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(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 engine->throwTypeError();
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(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(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(theirType, myQmlType));
431}
432
433ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
434{
435 // Keep this code in sync with ::virtualGet
436 PropertyKey id = engine->identifierTable->asPropertyKey(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(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(qobjectSingleton, false);
457 if (ddata && ddata->propertyCache) {
458 QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
459 if (property) {
460 ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
461 lookup->qobjectLookup.qmlTypeIc = This->internalClass();
462 lookup->qobjectLookup.ic = val->objectValue()->internalClass();
463 lookup->qobjectLookup.propertyCache = ddata->propertyCache;
464 lookup->qobjectLookup.propertyCache->addref();
465 lookup->qobjectLookup.propertyData = property;
466 lookup->getter = QQmlTypeWrapper::lookupSingletonProperty;
467 return lookup->getter(lookup, engine, *object);
468 }
469 // Fall through to base implementation
470 }
471 // Fall through to base implementation
472 }
473 // Fall through to base implementation
474 }
475 // Fall through to base implementation
476 }
477 // Fall through to base implementation
478 }
479 // Fall through to base implementation
480 }
481 return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
482}
483
484bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
485{
486 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
487}
488
489ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object)
490{
491 const auto revertLookup = [l, engine, &object]() {
492 l->qobjectLookup.propertyCache->release();
493 l->qobjectLookup.propertyCache = nullptr;
494 l->getter = Lookup::getterGeneric;
495 return Lookup::getterGeneric(l, engine, object);
496 };
497
498 // we can safely cast to a QV4::Object here. If object is something else,
499 // the internal class won't match
500 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
501 if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc)
502 return revertLookup();
503
504 Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o);
505
506 QQmlType type = This->type();
507 if (!type.isValid())
508 return revertLookup();
509
510 if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
511 return revertLookup();
512
513 QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
514 QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
515 Q_ASSERT(qobjectSingleton);
516
517 Scope scope(engine);
518 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
519 return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
520}
521
522void Heap::QQmlScopedEnumWrapper::destroy()
523{
524 QQmlType::derefHandle(typePrivate);
525 typePrivate = nullptr;
526 Object::destroy();
527}
528
529QQmlType Heap::QQmlScopedEnumWrapper::type() const
530{
531 return QQmlType(typePrivate);
532}
533
534ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
535{
536 Q_ASSERT(m->as<QQmlScopedEnumWrapper>());
537 if (!id.isString())
538 return Object::virtualGet(m, id, receiver, hasProperty);
539
540 const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m);
541 QV4::ExecutionEngine *v4 = resource->engine();
542 QV4::Scope scope(v4);
543 ScopedString name(scope, id.asStringOrSymbol());
544
545 QQmlType type = resource->d()->type();
546 int index = resource->d()->scopeEnumIndex;
547
548 bool ok = false;
549 int value = type.scopedEnumValue(QQmlEnginePrivate::get(v4->qmlEngine()), index, name, &ok);
550 if (hasProperty)
551 *hasProperty = ok;
552 if (ok)
553 return QV4::Value::fromInt32(value).asReturnedValue();
554
555 return Encode::undefined();
556}
557
558QT_END_NAMESPACE
559