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_STATIC_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->call = Lookup::Call::ContextGetterIdObject;
68 return OptionalReturnedValue(lookup->contextGetter(engine: v4, base));
69 } else if (originalLookup) {
70 originalLookup->call = Lookup::Call::ContextGetterInParentContextHierarchy;
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 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: v4->qmlEngine());
201 if (context->imports() && (name->startsWithUpper() || context->valueTypesAreAddressable())) {
202 // Search for attached properties, enums and imported scripts
203 QQmlTypeNameCache::Result r = context->imports()->query<QQmlImport::AllowRecursion>(
204 key: name, typeLoader: QQmlTypeLoader::get(engine: ep));
205
206 if (r.isValid()) {
207 if (hasProperty)
208 *hasProperty = true;
209 if (r.scriptIndex != -1) {
210 if (lookup) {
211 lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
212 lookup->call = Lookup::Call::ContextGetterScript;
213 return lookup->contextGetter(engine: v4, base);
214 }
215 QV4::ScopedObject scripts(scope, context->importedScripts());
216 if (scripts)
217 return scripts->get(idx: r.scriptIndex);
218 return QV4::Encode::null();
219 } else if (r.type.isValid()) {
220 if (lookup) {
221 bool isValueSingleton = false;
222 if (r.type.isSingleton()) {
223 QQmlEnginePrivate *e = QQmlEnginePrivate::get(e: v4->qmlEngine());
224 if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) {
225 e->singletonInstance<QObject*>(type: r.type);
226 lookup->qmlContextSingletonLookup.singletonObject.set(engine: v4,
227 heapObject: Value::fromReturnedValue(
228 val: QQmlTypeWrapper::create(v4, nullptr, r.type)
229 ).heapObject());
230 } else {
231 QJSValue singleton = e->singletonInstance<QJSValue>(type: r.type);
232
233 // QString values should already have been put on the engine heap at
234 // this point to manage their memory. We later assume this has already
235 // happened.
236 Q_ASSERT(!QJSValuePrivate::asQString(&singleton));
237
238 if (QV4::Value *val = QJSValuePrivate::takeManagedValue(jsval: &singleton)) {
239 lookup->qmlContextSingletonLookup.singletonObject.set(engine: v4, heapObject: val->heapObject());
240 } else {
241 lookup->qmlContextSingletonLookup.singletonValue = QJSValuePrivate::asReturnedValue(jsval: &singleton);
242 isValueSingleton = true;
243 }
244 }
245 lookup->call = isValueSingleton
246 ? Lookup::Call::ContextGetterValueSingleton
247 : Lookup::Call::ContextGetterSingleton;
248 return lookup->contextGetter(engine: 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.set(engine: v4, heapObject: result->heapObject());
257 lookup->call = Lookup::Call::ContextGetterType;
258 }
259 return result->asReturnedValue();
260 }
261
262 // Fall through
263 }
264
265 Lookup * const originalLookup = lookup;
266
267 Lookup::Call contextGetterCall = Lookup::Call::ContextGetterContextObjectProperty;
268
269 // minor optimization so we don't potentially try two property lookups on the same object
270 if (scopeObject == context->contextObject()) {
271 scopeObject = nullptr;
272 contextGetterCall = Lookup::Call::ContextGetterScopeObjectProperty;
273 }
274
275 QQmlRefPointer<QQmlContextData> outer = context;
276 while (context) {
277 if (outer == context) {
278 if (auto property = searchContextProperties(
279 v4, context, name, hasProperty, base, lookup, originalLookup, ep)) {
280 return *property;
281 }
282
283 outer = outer->parent();
284
285 if (const auto cu = context->typeCompilationUnit(); cu && cu->componentsAreBound()) {
286 // If components are bound in this CU, we can search the whole context hierarchy
287 // of the file. Bound components' contexts override their local properties.
288 // You also can't instantiate bound components outside of their creation
289 // context. Therefore this is safe.
290
291 for (;
292 outer && outer->typeCompilationUnit() == cu;
293 outer = outer->parent()) {
294 if (auto property = searchContextProperties(
295 v4, context: outer, name, hasProperty, base,
296 lookup: nullptr, originalLookup, ep)) {
297 return *property;
298 }
299 }
300 }
301 }
302
303 // Search scope object
304 if (scopeObject) {
305 bool hasProp = false;
306
307 const QQmlPropertyData *propertyData = nullptr;
308
309 QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine: v4, object: scopeObject));
310 QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(
311 engine: v4, qmlContext: context, wrapper: wrapper->d(), object: scopeObject, name,
312 flags: getQmlPropertyFlags(l: lookup), hasProperty: &hasProp, property: &propertyData));
313 if (hasProp) {
314 if (hasProperty)
315 *hasProperty = true;
316 if (base)
317 *base = wrapper;
318
319 if (lookup) {
320 QQmlData *ddata = QQmlData::get(object: scopeObject, create: false);
321 if (ddata && ddata->propertyCache) {
322 ScopedValue val(
323 scope,
324 base ? *base : Value::fromReturnedValue(
325 val: QV4::QObjectWrapper::wrap(engine: v4, object: scopeObject)));
326 if (QObjectMethod *method = result->as<QObjectMethod>()) {
327 QV4::setupQObjectMethodLookup(
328 lookup, ddata, propertyData, self: val->objectValue(),
329 method: method->d());
330 lookup->call = Lookup::Call::ContextGetterScopeObjectMethod;
331 } else {
332 QV4::setupQObjectLookup(
333 lookup, ddata, propertyData, self: val->objectValue());
334 lookup->call = Lookup::Call::ContextGetterScopeObjectProperty;
335 }
336 }
337 }
338
339 return result->asReturnedValue();
340 }
341 }
342 scopeObject = nullptr;
343
344
345 // Search context object
346 if (QObject *contextObject = context->contextObject()) {
347 bool hasProp = false;
348 const QQmlPropertyData *propertyData = nullptr;
349 QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine: v4, object: contextObject));
350 result = QV4::QObjectWrapper::getQmlProperty(
351 engine: v4, qmlContext: context, wrapper: wrapper->d(), object: contextObject, name, flags: getQmlPropertyFlags(l: lookup),
352 hasProperty: &hasProp, property: &propertyData);
353 if (hasProp) {
354 if (hasProperty)
355 *hasProperty = true;
356 if (base)
357 *base = wrapper;
358
359 if (lookup) {
360 QQmlData *ddata = QQmlData::get(object: contextObject, create: false);
361 if (ddata && ddata->propertyCache && lookup->call != contextGetterCall) {
362 ScopedValue val(
363 scope,
364 base ? *base : Value::fromReturnedValue(
365 val: QV4::QObjectWrapper::wrap(engine: v4, object: contextObject)));
366 if (QObjectMethod *method = result->as<QObjectMethod>()) {
367 setupQObjectMethodLookup(
368 lookup, ddata, propertyData, self: val->objectValue(),
369 method: method->d());
370 if (contextGetterCall == Lookup::Call::ContextGetterScopeObjectProperty)
371 lookup->call = Lookup::Call::ContextGetterScopeObjectMethod;
372 else
373 lookup->call = Lookup::Call::ContextGetterContextObjectMethod;
374 } else {
375 setupQObjectLookup(
376 lookup, ddata, propertyData, self: val->objectValue());
377 lookup->call = contextGetterCall;
378 }
379 }
380 } else if (originalLookup) {
381 originalLookup->call = Lookup::Call::ContextGetterInParentContextHierarchy;
382 }
383
384 return result->asReturnedValue();
385 }
386 }
387
388 context = context->parent();
389
390 // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
391 // the immediate QML context (of the .qml file).
392 lookup = nullptr;
393 }
394
395 // Do the generic JS lookup late in case of a non-JavaScript context.
396 // The scope, context, types etc should be able to override it.
397 if (!isJSContext && performLookup(result: &result, hasProperty, lookup: jsLookup))
398 return result->asReturnedValue();
399
400 // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
401 // true if we access properties of the global object.
402 if (originalLookup) {
403 // Try a lookup in the global object. It's theoretically possible to first find a property
404 // in the global object and then later a context property with the same name is added, but that
405 // never really worked as we used to detect access to global properties at type compile time anyway.
406 lookup = originalLookup;
407 result = lookup->resolveGlobalGetter(engine: v4);
408 if (lookup->call != Lookup::Call::GlobalGetterGeneric) {
409 if (hasProperty)
410 *hasProperty = true;
411 lookup->qmlContextGlobalLookup.getterTrampoline = lookup->call;
412 lookup->call = Lookup::Call::ContextGetterInGlobalObject;
413 return result->asReturnedValue();
414 }
415 lookup->call = Lookup::Call::ContextGetterGeneric;
416 } else {
417 if (performLookup(result: &result, hasProperty, lookup: globalLookup))
418 return result->asReturnedValue();
419 }
420
421 expressionContext->setUnresolvedNames(true);
422
423 return Encode::undefined();
424}
425
426ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
427{
428 Q_ASSERT(m->as<QQmlContextWrapper>());
429 const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
430 return getPropertyAndBase(resource: This, id, receiver, hasProperty, /*base*/nullptr);
431}
432
433bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
434{
435 Q_ASSERT(m->as<QQmlContextWrapper>());
436
437 if (id.isSymbol() || id.isArrayIndex())
438 return Object::virtualPut(m, id, value, receiver);
439
440 QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m);
441 ExecutionEngine *v4 = resource->engine();
442 QV4::Scope scope(v4);
443 if (scope.hasException())
444 return false;
445 QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource);
446
447 auto member = wrapper->internalClass()->findValueOrSetter(id);
448 if (member.index < UINT_MAX)
449 return wrapper->putValue(memberIndex: member.index, attrs: member.attrs, value);
450
451 // It's possible we could delay the calculation of the "actual" context (in the case
452 // of sub contexts) until it is definitely needed.
453 QQmlRefPointer<QQmlContextData> context = wrapper->getContext();
454 QQmlRefPointer<QQmlContextData> expressionContext = context;
455
456 if (!context)
457 return false;
458
459 // See QV8ContextWrapper::Getter for resolution order
460
461 QObject *scopeObject = wrapper->getScopeObject();
462 ScopedString name(scope, id.asStringOrSymbol());
463
464 while (context) {
465 // Search context properties
466 if (const int propertyIndex = context->propertyIndex(name); propertyIndex != -1) {
467 if (propertyIndex < context->numIdValues()) {
468 v4->throwError(message: QLatin1String("left-hand side of assignment operator is not an lvalue"));
469 return false;
470 }
471 return false;
472 }
473
474 // Search scope object
475 if (scopeObject &&
476 QV4::QObjectWrapper::setQmlProperty(engine: v4, qmlContext: context, object: scopeObject, name, flags: QV4::QObjectWrapper::CheckRevision, value))
477 return true;
478 scopeObject = nullptr;
479
480 // Search context object
481 if (context->contextObject() &&
482 QV4::QObjectWrapper::setQmlProperty(engine: v4, qmlContext: context, object: context->contextObject(), name,
483 flags: QV4::QObjectWrapper::CheckRevision, value))
484 return true;
485
486 context = context->parent();
487 }
488
489 expressionContext->setUnresolvedNames(true);
490
491 QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
492 QLatin1Char('"');
493 v4->throwError(message: error);
494 return false;
495}
496
497ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
498{
499 Scope scope(engine);
500 auto *func = engine->currentStackFrame->v4Function;
501 ScopedPropertyKey name(scope, engine->identifierTable->asPropertyKey(
502 str: func->compilationUnit->runtimeStrings[l->nameIndex]));
503
504 // Special hack for bounded signal expressions, where the parameters of signals are injected
505 // into the handler expression through the locals of the call context. So for onClicked: { ... }
506 // the parameters of the clicked signal are injected and we must allow for them to be found here
507 // before any other property from the QML context.
508 for (Heap::ExecutionContext *ctx = engine->currentContext()->d(); ctx; ctx = ctx->outer) {
509 if (ctx->type == Heap::ExecutionContext::Type_CallContext) {
510 const uint index = ctx->internalClass->indexOfValueOrGetter(id: name);
511 if (index < std::numeric_limits<uint>::max()) {
512 if (!func->detectedInjectedParameters) {
513 const auto location = func->sourceLocation();
514 qCWarning(lcQmlContext).nospace().noquote()
515 << location.sourceFile << ":" << location.line << ":" << location.column
516 << " Parameter \"" << name->toQString() << "\" is not declared."
517 << " Injection of parameters into signal handlers is deprecated."
518 << " Use JavaScript functions with formal parameters instead.";
519
520 // Don't warn over and over for the same function
521 func->detectedInjectedParameters = true;
522 }
523
524 return static_cast<Heap::CallContext *>(ctx)->locals[index].asReturnedValue();
525 }
526 }
527
528 // Skip only block and call contexts.
529 // Other contexts need a regular QML property lookup. See below.
530 if (ctx->type != Heap::ExecutionContext::Type_BlockContext && ctx->type != Heap::ExecutionContext::Type_CallContext)
531 break;
532 }
533
534 bool hasProperty = false;
535 ScopedValue result(scope);
536
537 Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext());
538 if (callingQmlContext) {
539 Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml());
540 result = QQmlContextWrapper::getPropertyAndBase(
541 resource: qmlContextWrapper, id: name, /*receiver*/nullptr, hasProperty: &hasProperty, base, lookup: l);
542 } else {
543 // Code path typical to worker scripts, compiled with lookups but no qml context.
544 result = l->resolveGlobalGetter(engine);
545 if (l->call != Lookup::Call::GlobalGetterGeneric) {
546 hasProperty = true;
547 l->qmlContextGlobalLookup.getterTrampoline = l->call;
548 l->call = Lookup::Call::ContextGetterInGlobalObject;
549 }
550 }
551 if (!hasProperty)
552 return engine->throwReferenceError(name: name->toQString());
553 return result->asReturnedValue();
554}
555
556ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
557{
558 Q_UNUSED(base);
559 Scope scope(engine);
560 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
561 if (!qmlContext)
562 return QV4::Encode::null();
563
564 QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext();
565 if (!context)
566 return QV4::Encode::null();
567
568 QV4::ScopedObject scripts(scope, context->importedScripts());
569 if (!scripts)
570 return QV4::Encode::null();
571 return scripts->get(idx: l->qmlContextScriptLookup.scriptIndex);
572}
573
574ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
575{
576 Q_UNUSED(engine);
577 Q_UNUSED(base);
578
579 return l->qmlContextSingletonLookup.singletonObject->asReturnedValue();
580}
581
582ReturnedValue QQmlContextWrapper::lookupValueSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
583{
584 Q_UNUSED(engine);
585 Q_UNUSED(base);
586
587 Q_ASSERT(l->qmlContextSingletonLookup.singletonObject == nullptr);
588 return l->qmlContextSingletonLookup.singletonValue;
589}
590
591ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
592{
593 Q_UNUSED(base);
594 Scope scope(engine);
595 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
596 if (!qmlContext)
597 return QV4::Encode::null();
598
599 QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext();
600 if (!context)
601 return QV4::Encode::null();
602
603 QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(e: engine->qmlEngine());
604 const int objectId = l->qmlContextIdObjectLookup.objectId;
605
606 if (qmlEngine->propertyCapture)
607 qmlEngine->propertyCapture->captureProperty(context->idValueBindings(index: objectId));
608
609 return QV4::QObjectWrapper::wrap(engine, object: context->idValue(index: objectId));
610}
611
612ReturnedValue QQmlContextWrapper::lookupIdObjectInParentContext(
613 Lookup *l, ExecutionEngine *engine, Value *base)
614{
615 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
616}
617
618static ReturnedValue revertObjectPropertyLookup(Lookup *l, ExecutionEngine *engine, Value *base)
619{
620 l->qobjectLookup.propertyCache->release();
621 l->qobjectLookup.propertyCache = nullptr;
622 l->call = Lookup::Call::ContextGetterGeneric;
623 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
624}
625
626static ReturnedValue revertObjectMethodLookup(Lookup *l, ExecutionEngine *engine, Value *base)
627{
628 l->qobjectMethodLookup.propertyCache->release();
629 l->qobjectMethodLookup.propertyCache = nullptr;
630 l->call = Lookup::Call::ContextGetterGeneric;
631 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
632}
633
634template<typename Call>
635ReturnedValue callWithScopeObject(ExecutionEngine *engine, Value *base, Call c)
636{
637 Scope scope(engine);
638 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
639 if (!qmlContext)
640 return QV4::Encode::undefined();
641
642 QObject *scopeObject = qmlContext->qmlScope();
643 if (!scopeObject)
644 return QV4::Encode::undefined();
645
646 if (QQmlData::wasDeleted(object: scopeObject))
647 return QV4::Encode::undefined();
648
649 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: scopeObject));
650
651 if (base)
652 *base = obj;
653
654 return c(obj);
655}
656
657ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
658{
659 return callWithScopeObject(engine, base, c: [l, engine, base](const Value &obj) {
660 const QObjectWrapper::Flags flags = l->forCall
661 ? QObjectWrapper::NoFlag
662 : QObjectWrapper::AttachMethods;
663 return QObjectWrapper::lookupPropertyGetterImpl(lookup: l, engine, object: obj, flags, revertLookup: [l, engine, base]() {
664 return revertObjectPropertyLookup(l, engine, base);
665 });
666 });
667}
668
669ReturnedValue QQmlContextWrapper::lookupScopeObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base)
670{
671 return callWithScopeObject(engine, base, c: [l, engine, base](const Value &obj) {
672 const QObjectWrapper::Flags flags = l->forCall
673 ? QObjectWrapper::NoFlag
674 : QObjectWrapper::AttachMethods;
675 return QObjectWrapper::lookupMethodGetterImpl(lookup: l, engine, object: obj, flags, revertLookup: [l, engine, base]() {
676 return revertObjectMethodLookup(l, engine, base);
677 });
678 });
679}
680
681template<typename Call>
682ReturnedValue callWithContextObject(ExecutionEngine *engine, Value *base, Call c)
683{
684 Scope scope(engine);
685 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
686 if (!qmlContext)
687 return QV4::Encode::undefined();
688
689 QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext();
690 if (!context)
691 return QV4::Encode::undefined();
692
693 QObject *contextObject = context->contextObject();
694 if (!contextObject)
695 return QV4::Encode::undefined();
696
697 if (QQmlData::wasDeleted(object: contextObject))
698 return QV4::Encode::undefined();
699
700 ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, object: contextObject));
701
702 if (base)
703 *base = obj;
704
705 return c(obj);
706}
707
708ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(
709 Lookup *l, ExecutionEngine *engine, Value *base)
710{
711 return callWithContextObject(engine, base, c: [l, engine, base](const Value &obj) {
712 const QObjectWrapper::Flags flags = l->forCall
713 ? QObjectWrapper::NoFlag
714 : QObjectWrapper::AttachMethods;
715 return QObjectWrapper::lookupPropertyGetterImpl(lookup: l, engine, object: obj, flags, revertLookup: [l, engine, base]() {
716 return revertObjectPropertyLookup(l, engine, base);
717 });
718 });
719}
720
721ReturnedValue QQmlContextWrapper::lookupContextObjectMethod(
722 Lookup *l, ExecutionEngine *engine, Value *base)
723{
724 return callWithContextObject(engine, base, c: [l, engine, base](const Value &obj) {
725 const QObjectWrapper::Flags flags = l->forCall
726 ? QObjectWrapper::NoFlag
727 : QObjectWrapper::AttachMethods;
728 return QObjectWrapper::lookupMethodGetterImpl(lookup: l, engine, object: obj, flags, revertLookup: [l, engine, base]() {
729 return revertObjectMethodLookup(l, engine, base);
730 });
731 });
732}
733
734ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base)
735{
736 return resolveQmlContextPropertyLookupGetter(l, engine, base);
737}
738
739ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
740{
741 Q_UNUSED(base);
742 ReturnedValue result = Lookup::doCallGlobal(
743 call: l->qmlContextGlobalLookup.getterTrampoline, lookup: l, engine);
744 // In the unlikely event of mutation of the global object, update the trampoline.
745 if (l->call != Lookup::Call::ContextGetterInGlobalObject) {
746 l->qmlContextGlobalLookup.getterTrampoline = l->call;
747 l->call = Lookup::Call::ContextGetterInGlobalObject;
748 }
749 return result;
750}
751
752ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base)
753{
754 Scope scope(engine);
755 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
756 if (!qmlContext)
757 return QV4::Encode::undefined();
758
759 QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext();
760 if (!context)
761 return QV4::Encode::undefined();
762
763 QQmlRefPointer<QQmlContextData> expressionContext = context;
764
765 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine->qmlEngine());
766
767 PropertyKey id =engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
768 runtimeStrings[l->nameIndex]);
769 ScopedString name(scope, id.asStringOrSymbol());
770
771 ScopedValue result(scope);
772
773 for (context = context->parent(); context; context = context->parent()) {
774 if (auto property = searchContextProperties(v4: engine, context, name, hasProperty: nullptr, base, lookup: nullptr, originalLookup: nullptr, ep))
775 return *property;
776
777 // Search context object
778 if (QObject *contextObject = context->contextObject()) {
779 bool hasProp = false;
780 QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine, object: contextObject));
781 result = QV4::QObjectWrapper::getQmlProperty(
782 engine, qmlContext: context, wrapper: wrapper->d(), object: contextObject, name, flags: getQmlPropertyFlags(l),
783 hasProperty: &hasProp);
784 if (hasProp) {
785 if (base)
786 *base = wrapper;
787
788 return result->asReturnedValue();
789 }
790 }
791 }
792
793 bool hasProp = false;
794 result = engine->globalObject->get(name, hasProperty: &hasProp);
795 if (hasProp)
796 return result->asReturnedValue();
797
798 expressionContext->setUnresolvedNames(true);
799
800 return Encode::undefined();
801}
802
803ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base)
804{
805 Scope scope(engine);
806 Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
807 if (!qmlContext)
808 return QV4::Encode::undefined();
809
810 QObject *scopeObject = qmlContext->qmlScope();
811 if (scopeObject && QQmlData::wasDeleted(object: scopeObject))
812 return QV4::Encode::undefined();
813
814 Heap::Base *heapObject = l->qmlTypeLookup.qmlTypeWrapper;
815 if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) {
816 l->qmlTypeLookup.qmlTypeWrapper.clear();
817 l->call = Lookup::Call::ContextGetterGeneric;
818 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
819 }
820
821 return Value::fromHeapObject(m: heapObject).asReturnedValue();
822}
823
824void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
825{
826 Heap::ExecutionContext::init(t: Heap::ExecutionContext::Type_QmlContext);
827 outer.set(e: internalClass->engine, newVal: outerContext->d());
828
829 this->activation.set(e: internalClass->engine, newVal: qml->d());
830}
831
832Heap::QmlContext *QmlContext::create(
833 ExecutionContext *parent, QQmlRefPointer<QQmlContextData> context,
834 QObject *scopeObject)
835{
836 Scope scope(parent);
837
838 Scoped<QQmlContextWrapper> qml(
839 scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(
840 args: std::move(context), args&: scopeObject));
841 Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(args&: parent, args&: qml);
842 Q_ASSERT(c->vtable() == staticVTable());
843 return c;
844}
845
846QT_END_NAMESPACE
847

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