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