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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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