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