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

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