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 "qv4qmlcontext_p.h"
41
42#include <private/qqmlengine_p.h>
43#include <private/qqmlcontext_p.h>
44
45#include <private/qv4engine_p.h>
46#include <private/qv4value_p.h>
47#include <private/qv4objectproto_p.h>
48#include <private/qv4mm_p.h>
49#include <private/qv4function_p.h>
50#include <private/qv4compileddata_p.h>
51#include <private/qqmltypewrapper_p.h>
52#include <private/qqmllistwrapper_p.h>
53#include <private/qqmljavascriptexpression_p.h>
54#include <private/qjsvalue_p.h>
55#include <private/qv4qobjectwrapper_p.h>
56#include <private/qv4module_p.h>
57#include <private/qv4lookup_p.h>
58#include <private/qv4identifiertable_p.h>
59
60QT_BEGIN_NAMESPACE
61
62using namespace QV4;
63
64DEFINE_OBJECT_VTABLE(QQmlContextWrapper);
65DEFINE_MANAGED_VTABLE(QmlContext);
66
67void Heap::QQmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject)
68{
69 Object::init();
70 this->context = new QQmlContextDataRef(context);
71 this->scopeObject.init(o: scopeObject);
72}
73
74void Heap::QQmlContextWrapper::destroy()
75{
76 delete context;
77 scopeObject.destroy();
78 Object::destroy();
79}
80
81static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name,
82 bool *hasProperty, Value *base, QV4::Lookup *lookup,
83 QV4::Lookup *originalLookup, QQmlEnginePrivate *ep)
84{
85 const QV4::IdentifierHash &properties = context->propertyNames();
86 if (properties.count() == 0)
87 return OptionalReturnedValue();
88
89 const int propertyIdx = properties.value(str: name);
90
91 if (propertyIdx == -1)
92 return OptionalReturnedValue();
93
94 if (propertyIdx < context->idValueCount) {
95 if (hasProperty)
96 *hasProperty = true;
97
98 if (lookup) {
99 lookup->qmlContextIdObjectLookup.objectId = propertyIdx;
100 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject;
101 return OptionalReturnedValue(lookup->qmlContextPropertyGetter(lookup, v4, base));
102 } else if (originalLookup) {
103 originalLookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInParentContextHierarchy;
104 }
105
106 if (ep->propertyCapture)
107 ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
108 return OptionalReturnedValue(QV4::QObjectWrapper::wrap(engine: v4, object: context->idValues[propertyIdx]));
109 }
110
111 QQmlContextPrivate *cp = context->asQQmlContextPrivate();
112
113 if (ep->propertyCapture)
114 ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex);
115
116 const QVariant &value = cp->propertyValues.at(i: propertyIdx);
117 if (hasProperty)
118 *hasProperty = true;
119 if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
120 QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
121 QQmlContextPrivate::context_count,
122 QQmlContextPrivate::context_at);
123 return OptionalReturnedValue(QmlListWrapper::create(engine: v4, prop, propType: qMetaTypeId<QQmlListProperty<QObject> >()));
124 }
125 return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(i: propertyIdx)));
126}
127
128ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup)
129{
130 if (!id.isString())
131 return Object::virtualGet(m: resource, id, receiver, hasProperty);
132
133 QV4::ExecutionEngine *v4 = resource->engine();
134 QV4::Scope scope(v4);
135
136 if (v4->callingQmlContext() != *resource->d()->context) {
137 if (resource->d()->module) {
138 Scoped<Module> module(scope, resource->d()->module);
139 bool hasProp = false;
140 ScopedValue value(scope, module->get(id, receiver, hasProperty: &hasProp));
141 if (hasProp) {
142 if (hasProperty)
143 *hasProperty = hasProp;
144 return value->asReturnedValue();
145 }
146 }
147
148 return Object::virtualGet(m: resource, id, receiver, hasProperty);
149 }
150
151 bool hasProp = false;
152 ScopedValue result(scope, Object::virtualGet(m: resource, id, receiver, hasProperty: &hasProp));
153 if (hasProp) {
154 if (hasProperty)
155 *hasProperty = hasProp;
156 return result->asReturnedValue();
157 }
158
159 // It's possible we could delay the calculation of the "actual" context (in the case
160 // of sub contexts) until it is definitely needed.
161 QQmlContextData *context = resource->getContext();
162 QQmlContextData *expressionContext = context;
163
164 if (!context) {
165 if (hasProperty)
166 *hasProperty = true;
167 return result->asReturnedValue();
168 }
169
170 // Search type (attached property/enum/imported scripts) names
171 // while (context) {
172 // Search context properties
173 // Search scope object
174 // Search context object
175 // context = context->parent
176 // }
177
178 QObject *scopeObject = resource->getScopeObject();
179
180 ScopedString name(scope, id.asStringOrSymbol());
181
182 const auto performGobalLookUp = [&result, v4, &name, hasProperty]() {
183 bool hasProp = false;
184 result = v4->globalObject->get(name, hasProperty: &hasProp);
185 if (hasProp) {
186 if (hasProperty)
187 *hasProperty = hasProp;
188 return true;
189 }
190 return false;
191 };
192
193 // If the scope object is a QAbstractDynamicMetaObject, then QMetaObject::indexOfProperty
194 // will call createProperty() on the QADMO and implicitly create the property. While that
195 // is questionable behavior, there are two use-cases that we support in the light of this:
196 //
197 // (1) The implicit creation of properties is necessary because it will also result in
198 // a recorded capture, which will allow a re-evaluation of bindings when the value
199 // is populated later. See QTBUG-35233 and the test-case in tst_qqmlpropertymap.
200 //
201 // (1) Looking up "console" in order to place a console.log() call for example must
202 // find the console instead of creating a new property. Therefore we prioritize the
203 // lookup in the global object here.
204 //
205 // Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap
206 // sub-class as QML type and then instantiates it in .qml.
207 if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) {
208 // all bets are off, so don't try to optimize any lookups
209 lookup = nullptr;
210 if (performGobalLookUp())
211 return result->asReturnedValue();
212 }
213
214 if (context->imports && name->startsWithUpper()) {
215 // Search for attached properties, enums and imported scripts
216 QQmlTypeNameCache::Result r = context->imports->query(name, recursionRestriction: QQmlImport::AllowRecursion);
217
218 if (r.isValid()) {
219 if (hasProperty)
220 *hasProperty = true;
221 if (r.scriptIndex != -1) {
222 if (lookup) {
223 lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
224 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript;
225 return lookup->qmlContextPropertyGetter(lookup, v4, base);
226 }
227 QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
228 if (scripts)
229 return scripts->get(idx: r.scriptIndex);
230 return QV4::Encode::null();
231 } else if (r.type.isValid()) {
232 if (lookup) {
233 if (r.type.isSingleton()) {
234 QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: v4->qmlEngine());
235 if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) {
236 e->singletonInstance<QObject*>(type: r.type);
237 lookup->qmlContextSingletonLookup.singleton =
238 static_cast<Heap::Object*>(
239 Value::fromReturnedValue(
240 val: QQmlTypeWrapper::create(v4, nullptr, r.type)
241 ).heapObject());
242 } else {
243 QJSValue singleton = e->singletonInstance<QJSValue>(type: r.type);
244 QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(e: v4, jsval: singleton));
245 lookup->qmlContextSingletonLookup.singleton = o->d();
246 }
247 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
248 return lookup->qmlContextPropertyGetter(lookup, v4, base);
249 }
250 }
251 result = QQmlTypeWrapper::create(v4, scopeObject, r.type);
252 } else if (r.importNamespace) {
253 result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
254 }
255 if (lookup) {
256 lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject());
257 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType;
258 }
259 return result->asReturnedValue();
260 }
261
262 // Fall through
263 }
264
265 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: v4->qmlEngine());
266 Lookup * const originalLookup = lookup;
267
268 decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
269
270 // minor optimization so we don't potentially try two property lookups on the same object
271 if (scopeObject == context->contextObject) {
272 scopeObject = nullptr;
273 contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
274 }
275
276 while (context) {
277 if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep))
278 return *property;
279
280 // Search scope object
281 if (scopeObject) {
282 bool hasProp = false;
283
284 QQmlPropertyData *propertyData = nullptr;
285 QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(engine: v4, qmlContext: context, object: scopeObject,
286 name, revisionMode: QV4::QObjectWrapper::CheckRevision, hasProperty: &hasProp, property: &propertyData));
287 if (hasProp) {
288 if (hasProperty)
289 *hasProperty = true;
290 if (base)
291 *base = QV4::QObjectWrapper::wrap(engine: v4, object: scopeObject);
292
293 if (lookup && propertyData) {
294 QQmlData *ddata = QQmlData::get(object: scopeObject, create: false);
295 if (ddata && ddata->propertyCache) {
296 ScopedValue val(scope, base ? *base : Value::fromReturnedValue(val: QV4::QObjectWrapper::wrap(engine: v4, object: scopeObject)));
297 QV4::setupQObjectLookup(lookup, ddata, propertyData, self: val->objectValue());
298 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
299 }
300 }
301
302 return result->asReturnedValue();
303 }
304 }
305 scopeObject = nullptr;
306
307
308 // Search context object
309 if (context->contextObject) {
310 bool hasProp = false;
311 QQmlPropertyData *propertyData = nullptr;
312 result = QV4::QObjectWrapper::getQmlProperty(engine: v4, qmlContext: context, object: context->contextObject,
313 name, revisionMode: QV4::QObjectWrapper::CheckRevision, hasProperty: &hasProp, property: &propertyData);
314 if (hasProp) {
315 if (hasProperty)
316 *hasProperty = true;
317 if (base)
318 *base = QV4::QObjectWrapper::wrap(engine: v4, object: context->contextObject);
319
320 if (propertyData) {
321 if (lookup) {
322 QQmlData *ddata = QQmlData::get(object: context->contextObject, create: false);
323 if (ddata && ddata->propertyCache) {
324 ScopedValue val(scope, base ? *base : Value::fromReturnedValue(val: QV4::QObjectWrapper::wrap(engine: v4, object: context->contextObject)));
325 QV4::setupQObjectLookup(lookup, ddata, propertyData,
326 self: val->objectValue());
327 lookup->qmlContextPropertyGetter = contextGetterFunction;
328 }
329 } else if (originalLookup) {
330 originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy;
331 }
332 }
333
334 return result->asReturnedValue();
335 }
336 }
337
338 context = context->parent;
339
340 // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
341 // the immediate QML context (of the .qml file).
342 lookup = nullptr;
343 }
344
345 // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
346 // true if we access properties of the global object.
347 if (originalLookup) {
348 // Try a lookup in the global object. It's theoretically possible to first find a property
349 // in the global object and then later a context property with the same name is added, but that
350 // never really worked as we used to detect access to global properties at type compile time anyway.
351 lookup = originalLookup;
352 result = lookup->resolveGlobalGetter(engine: v4);
353 if (lookup->globalGetter != Lookup::globalGetterGeneric) {
354 if (hasProperty)
355 *hasProperty = true;
356 lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter;
357 lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
358 return result->asReturnedValue();
359 }
360 lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
361 } else {
362 if (performGobalLookUp())
363 return result->asReturnedValue();
364 }
365
366 expressionContext->unresolvedNames = true;
367
368 return Encode::undefined();
369}
370
371ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
372{
373 Q_ASSERT(m->as<QQmlContextWrapper>());
374 const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
375 return getPropertyAndBase(resource: This, id, receiver, hasProperty, /*base*/nullptr);
376}
377
378bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
379{
380 Q_ASSERT(m->as<QQmlContextWrapper>());
381
382 if (id.isSymbol() || id.isArrayIndex())
383 return Object::virtualPut(m, id, value, receiver);
384
385 QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m);
386 ExecutionEngine *v4 = resource->engine();
387 QV4::Scope scope(v4);
388 if (scope.hasException())
389 return false;
390 QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource);
391
392 auto member = wrapper->internalClass()->findValueOrSetter(id);
393 if (member.index < UINT_MAX)
394 return wrapper->putValue(memberIndex: member.index, attrs: member.attrs, value);
395
396 // It's possible we could delay the calculation of the "actual" context (in the case
397 // of sub contexts) until it is definitely needed.
398 QQmlContextData *context = wrapper->getContext();
399 QQmlContextData *expressionContext = context;
400
401 if (!context)
402 return false;
403
404 // See QV8ContextWrapper::Getter for resolution order
405
406 QObject *scopeObject = wrapper->getScopeObject();
407 ScopedString name(scope, id.asStringOrSymbol());
408
409 while (context) {
410 const QV4::IdentifierHash &properties = context->propertyNames();
411 // Search context properties
412 if (properties.count()) {
413 const int propertyIndex = properties.value(str: name);
414 if (propertyIndex != -1) {
415 if (propertyIndex < context->idValueCount) {
416 v4->throwError(message: QLatin1String("left-hand side of assignment operator is not an lvalue"));
417 return false;
418 }
419 return false;
420 }
421 }
422
423 // Search scope object
424 if (scopeObject &&
425 QV4::QObjectWrapper::setQmlProperty(engine: v4, qmlContext: context, object: scopeObject, name, revisionMode: QV4::QObjectWrapper::CheckRevision, value))
426 return true;
427 scopeObject = nullptr;
428
429 // Search context object
430 if (context->contextObject &&
431 QV4::QObjectWrapper::setQmlProperty(engine: v4, qmlContext: context, object: context->contextObject, name, revisionMode: QV4::QObjectWrapper::CheckRevision, value))
432 return true;
433
434 context = context->parent;
435 }
436
437 expressionContext->unresolvedNames = true;
438
439 QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
440 QLatin1Char('"');
441 v4->throwError(message: error);
442 return false;
443}
444
445ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
446{
447 Scope scope(engine);
448 PropertyKey name =engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
449 runtimeStrings[l->nameIndex]);
450
451 // Special hack for bounded signal expressions, where the parameters of signals are injected
452 // into the handler expression through the locals of the call context. So for onClicked: { ... }
453 // the parameters of the clicked signal are injected and we must allow for them to be found here
454 // before any other property from the QML context.
455 for (Heap::ExecutionContext *ctx = engine->currentContext()->d(); ctx; ctx = ctx->outer) {
456 if (ctx->type == Heap::ExecutionContext::Type_CallContext) {
457 const uint index = ctx->internalClass->indexOfValueOrGetter(id: name);
458 if (index < std::numeric_limits<uint>::max())
459 return static_cast<Heap::CallContext *>(ctx)->locals[index].asReturnedValue();
460 }
461
462 // Skip only block and call contexts.
463 // Other contexts need a regular QML property lookup. See below.
464 if (ctx->type != Heap::ExecutionContext::Type_BlockContext && ctx->type != Heap::ExecutionContext::Type_CallContext)
465 break;
466 }
467
468 bool hasProperty = false;
469 ScopedValue result(scope);
470
471 Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext());
472 if (callingQmlContext) {
473 Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml());
474 result = QQmlContextWrapper::getPropertyAndBase(resource: qmlContextWrapper, id: name, /*receiver*/nullptr, hasProperty: &hasProperty,
475 base, lookup: l);
476 } else {
477 // Code path typical to worker scripts, compiled with lookups but no qml context.
478 result = l->resolveGlobalGetter(engine);
479 if (l->globalGetter != Lookup::globalGetterGeneric) {
480 hasProperty = true;
481 l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
482 l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
483 }
484 }
485 if (!hasProperty)
486 return engine->throwReferenceError(name: name.toQString());
487 return result->asReturnedValue();
488}
489
490ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
491{
492 Q_UNUSED(base)
493 Scope scope(engine);
494 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
495 if (!qmlContext)
496 return QV4::Encode::null();
497
498 QQmlContextData *context = qmlContext->qmlContext();
499 if (!context)
500 return QV4::Encode::null();
501
502 QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
503 if (!scripts)
504 return QV4::Encode::null();
505 return scripts->get(idx: l->qmlContextScriptLookup.scriptIndex);
506}
507
508ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
509{
510 Q_UNUSED(engine)
511 Q_UNUSED(base)
512 return Value::fromHeapObject(m: l->qmlContextSingletonLookup.singleton).asReturnedValue();
513}
514
515ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
516{
517 Q_UNUSED(base)
518 Scope scope(engine);
519 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
520 if (!qmlContext)
521 return QV4::Encode::null();
522
523 QQmlContextData *context = qmlContext->qmlContext();
524 if (!context)
525 return QV4::Encode::null();
526
527 QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(e: engine->qmlEngine());
528 const int objectId = l->qmlContextIdObjectLookup.objectId;
529
530 if (qmlEngine->propertyCapture)
531 qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings);
532
533 return QV4::QObjectWrapper::wrap(engine, object: context->idValues[objectId]);
534}
535
536ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
537{
538 Scope scope(engine);
539 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
540 if (!qmlContext)
541 return QV4::Encode::undefined();
542
543 QObject *scopeObject = qmlContext->qmlScope();
544 if (!scopeObject)
545 return QV4::Encode::undefined();
546
547 if (QQmlData::wasDeleted(object: scopeObject))
548 return QV4::Encode::undefined();
549
550 const auto revertLookup = [l, engine, base]() {
551 l->qobjectLookup.propertyCache->release();
552 l->qobjectLookup.propertyCache = nullptr;
553 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
554 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
555 };
556
557 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: scopeObject));
558
559 if (base)
560 *base = obj;
561
562 return QObjectWrapper::lookupGetterImpl(lookup: l, engine, object: obj, /*useOriginalProperty*/ true, revertLookup);
563}
564
565ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
566{
567 Scope scope(engine);
568 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
569 if (!qmlContext)
570 return QV4::Encode::undefined();
571
572 QQmlContextData *context = qmlContext->qmlContext();
573 if (!context)
574 return QV4::Encode::undefined();
575
576 QObject *contextObject = context->contextObject;
577 if (!contextObject)
578 return QV4::Encode::undefined();
579
580 if (QQmlData::wasDeleted(object: contextObject))
581 return QV4::Encode::undefined();
582
583 const auto revertLookup = [l, engine, base]() {
584 l->qobjectLookup.propertyCache->release();
585 l->qobjectLookup.propertyCache = nullptr;
586 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
587 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
588 };
589
590 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: contextObject));
591
592 if (base)
593 *base = obj;
594
595 return QObjectWrapper::lookupGetterImpl(lookup: l, engine, object: obj, /*useOriginalProperty*/ true, revertLookup);
596}
597
598ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
599{
600 Q_UNUSED(base);
601 ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine);
602 // In the unlikely event of mutation of the global object, update the trampoline.
603 if (l->qmlContextPropertyGetter != lookupInGlobalObject) {
604 l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
605 l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
606 }
607 return result;
608}
609
610ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base)
611{
612 Scope scope(engine);
613 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
614 if (!qmlContext)
615 return QV4::Encode::undefined();
616
617 QQmlContextData *context = qmlContext->qmlContext();
618 if (!context)
619 return QV4::Encode::undefined();
620
621 QQmlContextData *expressionContext = context;
622
623 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine->qmlEngine());
624
625 PropertyKey id =engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
626 runtimeStrings[l->nameIndex]);
627 ScopedString name(scope, id.asStringOrSymbol());
628
629 ScopedValue result(scope);
630
631 for (context = context->parent; context; context = context->parent) {
632 if (auto property = searchContextProperties(v4: engine, context, name, hasProperty: nullptr, base, lookup: nullptr, originalLookup: nullptr, ep))
633 return *property;
634
635 // Search context object
636 if (context->contextObject) {
637 bool hasProp = false;
638 result = QV4::QObjectWrapper::getQmlProperty(engine, qmlContext: context, object: context->contextObject,
639 name, revisionMode: QV4::QObjectWrapper::CheckRevision, hasProperty: &hasProp);
640 if (hasProp) {
641 if (base)
642 *base = QV4::QObjectWrapper::wrap(engine, object: context->contextObject);
643
644 return result->asReturnedValue();
645 }
646 }
647 }
648
649 bool hasProp = false;
650 result = engine->globalObject->get(name, hasProperty: &hasProp);
651 if (hasProp)
652 return result->asReturnedValue();
653
654 expressionContext->unresolvedNames = true;
655
656 return Encode::undefined();
657}
658
659ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base)
660{
661 Scope scope(engine);
662 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
663 if (!qmlContext)
664 return QV4::Encode::undefined();
665
666 QObject *scopeObject = qmlContext->qmlScope();
667 if (scopeObject && QQmlData::wasDeleted(object: scopeObject))
668 return QV4::Encode::undefined();
669
670 Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper;
671 if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) {
672 l->qmlTypeLookup.qmlTypeWrapper = nullptr;
673 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
674 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
675 }
676
677 return Value::fromHeapObject(m: heapObject).asReturnedValue();
678}
679
680void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
681{
682 Heap::ExecutionContext::init(t: Heap::ExecutionContext::Type_QmlContext);
683 outer.set(e: internalClass->engine, newVal: outerContext->d());
684
685 this->activation.set(e: internalClass->engine, newVal: qml->d());
686}
687
688Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject)
689{
690 Scope scope(parent);
691
692 Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(args: context, args: scopeObject));
693 Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(args: parent, args: qml);
694 Q_ASSERT(c->vtable() == staticVTable());
695 return c;
696}
697
698QT_END_NAMESPACE
699

source code of qtdeclarative/src/qml/jsruntime/qv4qmlcontext.cpp