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#ifndef QV4LOOKUP_H
4#define QV4LOOKUP_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include "qv4engine_p.h"
18#include "qv4object_p.h"
19#include "qv4internalclass_p.h"
20#include "qv4qmlcontext_p.h"
21#include <private/qqmltypewrapper_p.h>
22#include <private/qv4mm_p.h>
23
24QT_BEGIN_NAMESPACE
25
26namespace QV4 {
27
28namespace Heap {
29 struct QObjectMethod;
30}
31
32template <typename T, int PhantomTag>
33using HeapObjectWrapper = WriteBarrier::HeapObjectWrapper<T, PhantomTag>;
34
35// Note: We cannot hide the copy ctor and assignment operator of this class because it needs to
36// be trivially copyable. But you should never ever copy it. There are refcounted members
37// in there.
38struct Q_QML_EXPORT Lookup {
39 enum class Call: quint16 {
40 ContextGetterContextObjectMethod,
41 ContextGetterContextObjectProperty,
42 ContextGetterGeneric,
43 ContextGetterIdObject,
44 ContextGetterIdObjectInParentContext,
45 ContextGetterInGlobalObject,
46 ContextGetterInParentContextHierarchy,
47 ContextGetterScopeObjectMethod,
48 ContextGetterScopeObjectProperty,
49 ContextGetterScopeObjectPropertyFallback,
50 ContextGetterScript,
51 ContextGetterSingleton,
52 ContextGetterType,
53 ContextGetterValueSingleton,
54
55 GlobalGetterGeneric,
56 GlobalGetterProto,
57 GlobalGetterProtoAccessor,
58
59 Getter0Inline,
60 Getter0InlineGetter0Inline,
61 Getter0InlineGetter0MemberData,
62 Getter0MemberData,
63 Getter0MemberDataGetter0MemberData,
64 GetterAccessor,
65 GetterAccessorPrimitive,
66 GetterEnum,
67 GetterEnumValue,
68 GetterGeneric,
69 GetterIndexed,
70 GetterProto,
71 GetterProtoAccessor,
72 GetterProtoAccessorTwoClasses,
73 GetterProtoPrimitive,
74 GetterProtoTwoClasses,
75 GetterQObjectAttached,
76 GetterQObjectMethod,
77 GetterQObjectMethodFallback,
78 GetterQObjectProperty,
79 GetterQObjectPropertyFallback,
80 GetterSingletonMethod,
81 GetterSingletonProperty,
82 GetterStringLength,
83 GetterValueTypeProperty,
84
85 Setter0Inline,
86 Setter0MemberData,
87 Setter0Setter0,
88 SetterArrayLength,
89 SetterGeneric,
90 SetterInsert,
91 SetterQObjectProperty,
92 SetterQObjectPropertyFallback,
93 SetterValueTypeProperty,
94 };
95
96 // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null
97 // or that the least significant bit is 1 (see the Lookup::markObjects function)
98 union {
99 struct {
100 Heap::Base *h1;
101 Heap::Base *h2;
102 quintptr unused;
103 quintptr unused2;
104 } markDef;
105 struct {
106 HeapObjectWrapper<Heap::InternalClass, 0> ic;
107 quintptr unused;
108 uint index;
109 uint offset;
110 } objectLookup;
111 struct {
112 quintptr protoId;
113 quintptr _unused;
114 const Value *data;
115 const QtPrivate::QMetaTypeInterface *metaType;
116 } protoLookup;
117 struct {
118 HeapObjectWrapper<Heap::InternalClass, 1> ic;
119 HeapObjectWrapper<Heap::InternalClass, 2> ic2;
120 uint offset;
121 uint offset2;
122 } objectLookupTwoClasses;
123 struct {
124 quintptr protoId;
125 quintptr protoId2;
126 const Value *data;
127 const Value *data2;
128 } protoLookupTwoClasses;
129 struct {
130 // Make sure the next two values are in sync with protoLookup
131 quintptr protoId;
132 HeapObjectWrapper<Heap::Object, 3> proto;
133 const Value *data;
134 quintptr type;
135 } primitiveLookup;
136 struct {
137 HeapObjectWrapper<Heap::InternalClass, 4> newClass;
138 quintptr protoId;
139 uint offset;
140 uint unused;
141 } insertionLookup;
142 struct {
143 quintptr _unused;
144 quintptr _unused2;
145 uint index;
146 uint unused;
147 } indexedLookup;
148 struct {
149 HeapObjectWrapper<Heap::InternalClass, 5> ic;
150 HeapObjectWrapper<Heap::InternalClass, 6> qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper
151 const QQmlPropertyCache *propertyCache;
152 const QQmlPropertyData *propertyData;
153 } qobjectLookup;
154 struct {
155 HeapObjectWrapper<Heap::InternalClass, 7> ic;
156 HeapObjectWrapper<Heap::QObjectMethod, 8> method;
157 const QQmlPropertyCache *propertyCache;
158 const QQmlPropertyData *propertyData;
159 } qobjectMethodLookup;
160 struct {
161 quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc
162 quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
163 int coreIndex;
164 int notifyIndex;
165 } qobjectFallbackLookup;
166 struct {
167 HeapObjectWrapper<Heap::InternalClass, 9> ic;
168 quintptr metaObject; // a (const QMetaObject* & 1) or nullptr
169 const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial
170 quint16 coreIndex;
171 bool isFunction;
172 bool isEnum;
173 } qgadgetLookup;
174 struct {
175 quintptr unused1;
176 quintptr unused2;
177 int scriptIndex;
178 } qmlContextScriptLookup;
179 struct {
180 HeapObjectWrapper<Heap::Base, 10> singletonObject;
181 quintptr unused2;
182 QV4::ReturnedValue singletonValue;
183 } qmlContextSingletonLookup;
184 struct {
185 quintptr unused1;
186 quintptr unused2;
187 int objectId;
188 } qmlContextIdObjectLookup;
189 struct {
190 // Same as protoLookup, as used for global lookups
191 quintptr reserved1;
192 quintptr reserved2;
193 quintptr reserved3;
194 Call getterTrampoline;
195 } qmlContextGlobalLookup;
196 struct {
197 HeapObjectWrapper<Heap::Base, 11> qmlTypeWrapper;
198 quintptr unused2;
199 } qmlTypeLookup;
200 struct {
201 HeapObjectWrapper<Heap::InternalClass, 12> ic;
202 quintptr unused;
203 ReturnedValue encodedEnumValue;
204 const QtPrivate::QMetaTypeInterface *metaType;
205 } qmlEnumValueLookup;
206 struct {
207 HeapObjectWrapper<Heap::InternalClass, 13> ic;
208 HeapObjectWrapper<Heap::Object, 14> qmlEnumWrapper;
209 } qmlEnumWrapperLookup;
210 };
211
212 Call call;
213 quint16 padding;
214
215 uint nameIndex: 28; // Same number of bits we store in the compilation unit for name indices
216 uint forCall: 1; // Whether we are looking up a value in order to call it right away
217 uint asVariant: 1; // Whether all types are to be converted from/to QVariant
218 uint reserved: 2;
219
220 ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object);
221 ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object);
222 ReturnedValue resolveGlobalGetter(ExecutionEngine *engine);
223 void resolveProtoGetter(PropertyKey name, const Heap::Object *proto);
224
225 static ReturnedValue getterGeneric(Lookup *lookup, ExecutionEngine *engine, const Value &object);
226 static ReturnedValue getterTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object);
227 static ReturnedValue getterFallback(Lookup *lookup, ExecutionEngine *engine, const Value &object);
228
229 static ReturnedValue getter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object);
230 static ReturnedValue getter0Inline(Lookup *lookup, ExecutionEngine *engine, const Value &object);
231 static ReturnedValue getterProto(Lookup *lookup, ExecutionEngine *engine, const Value &object);
232 static ReturnedValue getter0Inlinegetter0Inline(Lookup *lookup, ExecutionEngine *engine, const Value &object);
233 static ReturnedValue getter0Inlinegetter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object);
234 static ReturnedValue getter0MemberDatagetter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object);
235 static ReturnedValue getterProtoTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object);
236 static ReturnedValue getterAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object);
237 static ReturnedValue getterProtoAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object);
238 static ReturnedValue getterProtoAccessorTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object);
239 static ReturnedValue getterIndexed(Lookup *lookup, ExecutionEngine *engine, const Value &object);
240 static ReturnedValue getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object);
241 static ReturnedValue getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object);
242 static ReturnedValue getterFallbackMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object);
243
244 static ReturnedValue getterValueType(Lookup *lookup, ExecutionEngine *engine, const Value &object);
245
246 static ReturnedValue primitiveGetterProto(Lookup *lookup, ExecutionEngine *engine, const Value &object);
247 static ReturnedValue primitiveGetterAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object);
248 static ReturnedValue stringLengthGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object);
249
250 static ReturnedValue globalGetterGeneric(Lookup *lookup, ExecutionEngine *engine);
251 static ReturnedValue globalGetterProto(Lookup *lookup, ExecutionEngine *engine);
252 static ReturnedValue globalGetterProtoAccessor(Lookup *lookup, ExecutionEngine *engine);
253
254 bool resolveSetter(ExecutionEngine *engine, Object *object, const Value &value);
255 static bool setterGeneric(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
256 Q_NEVER_INLINE static bool setterTwoClasses(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
257 static bool setterFallback(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
258 static bool setter0MemberData(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
259 static bool setter0Inline(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
260 static bool setter0setter0(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
261 static bool setterInsert(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
262 static bool setterQObject(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
263 static bool arrayLengthSetter(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value);
264
265 void markObjects(MarkStack *stack) {
266 if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1))
267 markDef.h1->mark(markStack: stack);
268 if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1))
269 markDef.h2->mark(markStack: stack);
270 }
271
272 ReturnedValue contextGetter(ExecutionEngine *engine, Value *base)
273 {
274 switch (call) {
275 case Call::ContextGetterContextObjectMethod:
276 return QQmlContextWrapper::lookupContextObjectMethod(l: this, engine, base);
277 case Call::ContextGetterContextObjectProperty:
278 return QQmlContextWrapper::lookupContextObjectProperty(l: this, engine, base);
279 case Call::ContextGetterGeneric:
280 return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l: this, engine, base);
281 case Call::ContextGetterIdObject:
282 return QQmlContextWrapper::lookupIdObject(l: this, engine, base);
283 case Call::ContextGetterIdObjectInParentContext:
284 return QQmlContextWrapper::lookupIdObjectInParentContext(l: this, engine, base);
285 case Call::ContextGetterInGlobalObject:
286 return QQmlContextWrapper::lookupInGlobalObject(l: this, engine, base);
287 case Call::ContextGetterInParentContextHierarchy:
288 return QQmlContextWrapper::lookupInParentContextHierarchy(l: this, engine, base);
289 case Call::ContextGetterScopeObjectMethod:
290 return QQmlContextWrapper::lookupScopeObjectMethod(l: this, engine, base);
291 case Call::ContextGetterScopeObjectProperty:
292 return QQmlContextWrapper::lookupScopeObjectProperty(l: this, engine, base);
293 case Call::ContextGetterSingleton:
294 return QQmlContextWrapper::lookupSingleton(l: this, engine, base);
295 case Call::ContextGetterScript:
296 return QQmlContextWrapper::lookupScript(l: this, engine, base);
297 case Call::ContextGetterType:
298 return QQmlContextWrapper::lookupType(l: this, engine, base);
299 case Call::ContextGetterValueSingleton:
300 return QQmlContextWrapper::lookupValueSingleton(l: this, engine, base);
301 default:
302 break;
303 }
304
305 Q_UNREACHABLE_RETURN(Encode::undefined());
306 }
307
308 static ReturnedValue doCallGlobal(Call call, Lookup *lookup, ExecutionEngine *engine)
309 {
310 switch (call) {
311 case Call::GlobalGetterGeneric:
312 return globalGetterGeneric(lookup, engine);
313 case Call::GlobalGetterProto:
314 return globalGetterProto(lookup, engine);
315 case Call::GlobalGetterProtoAccessor:
316 return globalGetterProtoAccessor(lookup, engine);
317 default:
318 break;
319 }
320
321 Q_UNREACHABLE_RETURN(Encode::undefined());
322 }
323
324 ReturnedValue globalGetter(ExecutionEngine *engine)
325 {
326 return doCallGlobal(call, lookup: this, engine);
327 }
328
329 ReturnedValue getter(ExecutionEngine *engine, const Value &object)
330 {
331 switch (call) {
332 case Call::Getter0Inline:
333 return getter0Inline(lookup: this, engine, object);
334 case Call::Getter0InlineGetter0Inline:
335 return getter0Inlinegetter0Inline(lookup: this, engine, object);
336 case Call::Getter0InlineGetter0MemberData:
337 return getter0Inlinegetter0MemberData(lookup: this, engine, object);
338 case Call::Getter0MemberData:
339 return getter0MemberData(lookup: this, engine, object);
340 case Call::Getter0MemberDataGetter0MemberData:
341 return getter0MemberDatagetter0MemberData(lookup: this, engine, object);
342 case Call::GetterAccessor:
343 return getterAccessor(lookup: this, engine, object);
344 case Call::GetterAccessorPrimitive:
345 return primitiveGetterAccessor(lookup: this, engine, object);
346 case Call::GetterEnumValue:
347 return QQmlTypeWrapper::lookupEnumValue(l: this, engine, base: object);
348 case Call::GetterQObjectPropertyFallback:
349 return getterFallback(lookup: this, engine, object);
350 case Call::GetterQObjectMethodFallback:
351 return getterFallbackMethod(lookup: this, engine, object);
352 case Call::GetterGeneric:
353 return getterGeneric(lookup: this, engine, object);
354 case Call::GetterIndexed:
355 return getterIndexed(lookup: this, engine, object);
356 case Call::GetterProto:
357 return getterProto(lookup: this, engine, object);
358 case Call::GetterProtoAccessor:
359 return getterProtoAccessor(lookup: this, engine, object);
360 case Call::GetterProtoAccessorTwoClasses:
361 return getterProtoAccessorTwoClasses(lookup: this, engine, object);
362 case Call::GetterProtoPrimitive:
363 return primitiveGetterProto(lookup: this, engine, object);
364 case Call::GetterProtoTwoClasses:
365 return getterProtoTwoClasses(lookup: this, engine, object);
366 case Call::GetterQObjectProperty:
367 return getterQObject(lookup: this, engine, object);
368 case Call::GetterQObjectAttached:
369 // TODO: more specific implementation for interpreter / JIT
370 return getterGeneric(lookup: this, engine, object);
371 case Call::GetterQObjectMethod:
372 return getterQObjectMethod(lookup: this, engine, object);
373 case Call::GetterSingletonMethod:
374 return QQmlTypeWrapper::lookupSingletonMethod(l: this, engine, base: object);
375 case Call::GetterSingletonProperty:
376 return QQmlTypeWrapper::lookupSingletonProperty(l: this, engine, base: object);
377 case Call::GetterStringLength:
378 return stringLengthGetter(lookup: this, engine, object);
379 case Call::GetterValueTypeProperty:
380 return getterValueType(lookup: this, engine, object);
381 case Call::GetterEnum:
382 return QQmlTypeWrapper::lookupEnum(l: this, engine, base: object);
383 default:
384 break;
385 }
386
387 Q_UNREACHABLE_RETURN(Encode::undefined());
388 }
389
390 bool setter(ExecutionEngine *engine, Value &object, const Value &value)
391 {
392 switch (call) {
393 case Call::Setter0Inline:
394 return setter0Inline(lookup: this, engine, object, value);
395 case Call::Setter0MemberData:
396 return setter0MemberData(lookup: this, engine, object, value);
397 case Call::Setter0Setter0:
398 return setter0setter0(lookup: this, engine, object, value);
399 case Call::SetterArrayLength:
400 return arrayLengthSetter(lookup: this, engine, object, value);
401 case Call::SetterQObjectPropertyFallback:
402 return setterFallback(lookup: this, engine, object, value);
403 case Call::SetterGeneric:
404 return setterGeneric(lookup: this, engine, object, value);
405 case Call::SetterInsert:
406 return setterInsert(lookup: this, engine, object, value);
407 case Call::SetterQObjectProperty:
408 return setterQObject(lookup: this, engine, object, value);
409 case Call::SetterValueTypeProperty:
410 // TODO: more specific implementation for interpreter / JIT
411 return setterFallback(lookup: this, engine, object, value);
412 default:
413 break;
414 }
415
416 Q_UNREACHABLE_RETURN(Encode::undefined());
417 }
418
419 void releasePropertyCache()
420 {
421 switch (call) {
422 case Call::ContextGetterContextObjectProperty:
423 case Call::ContextGetterScopeObjectProperty:
424 case Call::GetterQObjectProperty:
425 case Call::GetterSingletonProperty:
426 case Call::SetterQObjectProperty:
427 if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache)
428 pc->release();
429 break;
430 case Call::ContextGetterContextObjectMethod:
431 case Call::ContextGetterScopeObjectMethod:
432 case Call::GetterQObjectMethod:
433 case Call::GetterSingletonMethod:
434 if (const QQmlPropertyCache *pc = qobjectMethodLookup.propertyCache)
435 pc->release();
436 break;
437 default:
438 break;
439 }
440 }
441};
442
443Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
444
445inline void setupQObjectLookup(
446 Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData)
447{
448 lookup->releasePropertyCache();
449 Q_ASSERT(!ddata->propertyCache.isNull());
450 lookup->qobjectLookup.propertyCache = ddata->propertyCache.data();
451 lookup->qobjectLookup.propertyCache->addref();
452 lookup->qobjectLookup.propertyData = propertyData;
453}
454
455inline void setupQObjectLookup(
456 Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData,
457 const Object *self)
458{
459 setupQObjectLookup(lookup, ddata, propertyData);
460 lookup->qobjectLookup.ic.set(engine: self->engine(), heapObject: self->internalClass());
461}
462
463
464inline void setupQObjectLookup(
465 Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData,
466 const Object *self, const Object *qmlType)
467{
468 setupQObjectLookup(lookup, ddata, propertyData, self);
469 lookup->qobjectLookup.qmlTypeIc.set(engine: self->engine(), heapObject: qmlType->internalClass());
470}
471
472// template parameter is an ugly trick to avoid pulling in the QObjectMethod header here
473template<typename QObjectMethod = Heap::QObjectMethod>
474inline void setupQObjectMethodLookup(
475 Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData,
476 const Object *self, QObjectMethod *method)
477{
478 lookup->releasePropertyCache();
479 Q_ASSERT(!ddata->propertyCache.isNull());
480 auto engine = self->engine();
481 lookup->qobjectMethodLookup.method.set(engine, method);
482 lookup->qobjectMethodLookup.ic.set(engine, heapObject: self->internalClass());
483 lookup->qobjectMethodLookup.propertyCache = ddata->propertyCache.data();
484 lookup->qobjectMethodLookup.propertyCache->addref();
485 lookup->qobjectMethodLookup.propertyData = propertyData;
486}
487
488inline bool qualifiesForMethodLookup(const QQmlPropertyData *propertyData)
489{
490 return propertyData->isFunction()
491 && !propertyData->isSignalHandler() // TODO: Optimize SignalHandler, too
492 && !propertyData->isVMEFunction() // Handled by QObjectLookup
493 && !propertyData->isVarProperty();
494}
495
496}
497
498QT_END_NAMESPACE
499
500#endif
501

source code of qtdeclarative/src/qml/jsruntime/qv4lookup_p.h