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
qv4referenceobject_p.h
Go to the documentation of this file.
1// Copyright (C) 2022 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
5#ifndef QV4REFERENCEOBJECT_P_H
6#define QV4REFERENCEOBJECT_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <private/qv4object_p.h>
20#include <private/qv4stackframe_p.h>
21#include <private/qqmlnotifier_p.h>
22#include <private/qv4qobjectwrapper_p.h>
23
25
26namespace QV4 {
27namespace Heap {
28
29struct ReferenceObject;
31 ReferenceObjectEndpoint(ReferenceObject* reference)
33 reference(reference)
34 {}
35
36 ReferenceObject* reference;
37};
38
39#define ReferenceObjectMembers(class, Member)
40 Member(class, Pointer, Object *, m_object)
41
42DECLARE_HEAP_OBJECT(ReferenceObject, Object) {
43 DECLARE_MARKOBJECTS(ReferenceObject);
44
45 enum Flag : quint8 {
46 NoFlag = 0,
47 CanWriteBack = 1 << 0,
48 IsVariant = 1 << 1,
49 EnforcesLocation = 1 << 2,
50 IsDirty = 1 << 3,
51 };
52 Q_DECLARE_FLAGS(Flags, Flag);
53
54 void init(Object *object, int property, Flags flags)
55 {
56 auto connectToNotifySignal = [this](QObject* obj, int property, QQmlEngine* engine) {
57 Q_ASSERT(obj);
58 Q_ASSERT(engine);
59
60 Q_ASSERT(!referenceEndpoint);
61 Q_ASSERT(!bindableNotifier);
62
63 referenceEndpoint = new ReferenceObjectEndpoint(this);
64 referenceEndpoint->connect(
65 obj,
66 // Connect and signal emission work on "signal
67 // indexe"s. Those are different from "method
68 // indexes".
69 // The public MetaObject interface can, generally,
70 // give us the "method index" of the notify
71 // signal.
72 // Quite unintuitively, this is true for
73 // "notifySignalIndex".
74 // As the "method index" and the "signal index"
75 // can be different, connecting the "method index"
76 // of the notify signal can incur in issues when
77 // the signal is being emitted and checking for
78 // connected endpoints.
79 // For example, we might be connected to the
80 // "method index" of the notify signal for the
81 // property and end up checking for the
82 // subscribers of a different signal when the
83 // notify signal is emitted, due to the different
84 // meaning of the same index.
85 // Thus we pass by the private interface to ensure
86 // that we are connecting based on the "signal
87 // index" instead.
88 QMetaObjectPrivate::signalIndex(obj->metaObject()->property(property).notifySignal()),
89 engine);
90 // When the object that is being referenced is destroyed, we
91 // need to ensure that one additional read is performed to
92 // invalidate the data we hold.
93 // As the object might be destroyed in a way that doesn't
94 // trigger the notify signal for the relevant property, we react
95 // directly to the destruction itself.
96 // We use a plain connection instead of a QQmlNotifierEndpoint
97 // based connection as, currently, declarative-side signals are
98 // always discarded during destruction (see
99 // QQmlData::signalEmitted).
100 // In theory it should be possible to relax that condition for
101 // the destroy signal specifically, which should allow a more
102 // optimized way of connecting.
103 // Nonetheless this seems to be the only place where we have
104 // this kind of need, and thus go for the simpler solution,
105 // which can be changed later if the need arise.
106 new(onDelete) QMetaObject::Connection(QObject::connect(obj, &QObject::destroyed, [this](){ setDirty(true); }));
107 };
108
109 auto connectToBindable = [this](QObject* obj, int property, QQmlEngine* engine) {
110 Q_ASSERT(obj);
111 Q_ASSERT(engine);
112
113 Q_ASSERT(!referenceEndpoint);
114 Q_ASSERT(!bindableNotifier);
115
116 bindableNotifier = new QPropertyNotifier(obj->metaObject()->property(property).bindable(obj).addNotifier([this](){ setDirty(true); }));
117 new(onDelete) QMetaObject::Connection(QObject::connect(obj, &QObject::destroyed, [this](){ setDirty(true); }));
118 };
119
120 setObject(object);
121 m_property = property;
122 m_flags = flags;
123
124 while (object &&
125 object->internalClass->vtable->type != Managed::Type_V4QObjectWrapper &&
126 object->internalClass->vtable->type != Managed::Type_QMLTypeWrapper)
127 {
128 if (!(object->internalClass->vtable->type == Managed::Type_V4ReferenceObject) &&
129 !(object->internalClass->vtable->type == Managed::Type_V4Sequence) &&
130 !(object->internalClass->vtable->type == Managed::Type_DateObject) &&
131 !(object->internalClass->vtable->type == Managed::Type_QMLValueTypeWrapper))
132 {
133 break;
134 }
135
136 property = static_cast<QV4::Heap::ReferenceObject*>(object)->property();
137 object = static_cast<QV4::Heap::ReferenceObject*>(object)->object();
138 }
139
140 if (object && object->internalClass->vtable->type == Managed::Type_V4QObjectWrapper)
141 {
142 auto wrapper = static_cast<QV4::Heap::QObjectWrapper*>(object);
143 QObject* obj = wrapper->object();
144
145 if (obj->metaObject()->property(property).isBindable() && internalClass->engine->qmlEngine())
146 connectToBindable(obj, property, internalClass->engine->qmlEngine());
147 else if (obj->metaObject()->property(property).hasNotifySignal() && internalClass->engine->qmlEngine())
148 connectToNotifySignal(obj, property, internalClass->engine->qmlEngine());
149 }
150
151 if (object && object->internalClass->vtable->type == Managed::Type_QMLTypeWrapper) {
152 auto wrapper = static_cast<QV4::Heap::QQmlTypeWrapper*>(object);
153
154 Scope scope(internalClass->engine);
155 Scoped<QV4::QQmlTypeWrapper> scopedWrapper(scope, wrapper);
156 QObject* obj = scopedWrapper->object();
157
158 if (obj->metaObject()->property(property).isBindable() && internalClass->engine->qmlEngine())
159 connectToBindable(obj, property, internalClass->engine->qmlEngine());
160 else if (obj->metaObject()->property(property).hasNotifySignal() && internalClass->engine->qmlEngine())
161 connectToNotifySignal(obj, property, internalClass->engine->qmlEngine());
162 }
163
164 // If we could not connect to anything we don't have a way to
165 // dirty on-demand and thus should be in an always dirty state
166 // to ensure that reads go through.
167 if (!isConnected())
168 setDirty(true);
169
170 Object::init();
171 }
172
173 Flags flags() const { return Flags(m_flags); }
174
175 Object *object() const { return m_object.get(); }
176 void setObject(Object *object) { m_object.set(internalClass->engine, object); }
177
178 int property() const { return m_property; }
179
180 bool canWriteBack() const { return hasFlag(CanWriteBack); }
181 bool isVariant() const { return hasFlag(IsVariant); }
182 bool enforcesLocation() const { return hasFlag(EnforcesLocation); }
183
184 void setLocation(const Function *function, quint16 statement)
185 {
186 m_function = function;
187 m_statementIndex = statement;
188 }
189
190 const Function *function() const { return m_function; }
191 quint16 statementIndex() const { return m_statementIndex; }
192
193 bool isAttachedToProperty() const
194 {
195 if (enforcesLocation()) {
196 if (CppStackFrame *frame = internalClass->engine->currentStackFrame) {
197 if (frame->v4Function != function() || frame->statementNumber() != statementIndex())
198 return false;
199 } else {
200 return false;
201 }
202 }
203
204 return true;
205 }
206
207 bool isReference() const { return m_object; }
208
209 bool isDirty() const { return hasFlag(IsDirty); }
210 void setDirty(bool dirty) { setFlag(IsDirty, dirty); }
211 bool isConnected() {
212 return (referenceEndpoint && referenceEndpoint->isConnected()) || bindableNotifier;
213 }
214
215 void destroy() {
216 // If we allocated any connection then we must have connected
217 // to the destroyed signal too, and we should clean it up.
218 if (referenceEndpoint || bindableNotifier) {
219 QObject::disconnect(*reinterpret_cast<QMetaObject::Connection*>(&onDelete));
220 std::destroy_at(reinterpret_cast<QMetaObject::Connection*>(&onDelete));
221 }
222
223 if (referenceEndpoint)
224 delete referenceEndpoint;
225
226 if (bindableNotifier)
227 delete bindableNotifier;
228 }
229
230private:
231
232 bool hasFlag(Flag flag) const
233 {
234 return m_flags & quint8(flag);
235 }
236
237 void setFlag(Flag flag, bool set)
238 {
239 m_flags = set ? (m_flags | quint8(flag)) : (m_flags & ~quint8(flag));
240 }
241
242 const Function *m_function;
243 int m_property;
244 quint16 m_statementIndex;
245 quint8 m_flags;
246 ReferenceObjectEndpoint* referenceEndpoint;
247 QPropertyNotifier* bindableNotifier;
248 // We need to store an handle if we connect to the destroyed
249 // signal so that we can disconnect from it. To avoid yet
250 // another allocation, considering that
251 // QMetaObject::Connection is not trivial, we store it in
252 // block memory.
253 alignas(alignof(QMetaObject::Connection))
254 std::byte onDelete[sizeof(QMetaObject::Connection)];
255};
256
258
259} // namespace Heap
260
261
262struct ReferenceObject : public Object
263{
264 V4_OBJECT2(ReferenceObject, Object)
267
268public:
269 static constexpr const int AllProperties = -1;
270
271 template<typename HeapObject>
272 static bool readReference(HeapObject *ref)
273 {
274 if (!ref->object())
275 return false;
276
277 if (!ref->isDirty())
278 return true;
279
280 QV4::Scope scope(ref->internalClass->engine);
281 QV4::ScopedObject object(scope, ref->object());
282
283 bool wasRead = false;
284 if (ref->isVariant()) {
285 QVariant variant;
286 void *a[] = { &variant };
287 wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a)
288 && ref->setVariant(variant);
289 } else {
290 void *a[] = { ref->storagePointer() };
291 wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a);
292 }
293
294 ref->setDirty(!ref->isConnected() || !wasRead);
295 return wasRead;
296 }
297
298 template<typename HeapObject>
299 static bool writeBack(HeapObject *ref, int internalIndex = AllProperties)
300 {
301 if (!ref->object() || !ref->canWriteBack())
302 return false;
303
304 QV4::Scope scope(ref->internalClass->engine);
305 QV4::ScopedObject object(scope, ref->object());
306
307 int flags = QQmlPropertyData::HasInternalIndex;
308 int status = -1;
309 if (ref->isVariant()) {
310 QVariant variant = ref->toVariant();
311 void *a[] = { &variant, nullptr, &status, &flags, &internalIndex };
312 return object->metacall(QMetaObject::WriteProperty, ref->property(), a);
313 }
314
315 void *a[] = { ref->storagePointer(), nullptr, &status, &flags, &internalIndex };
316 return object->metacall(QMetaObject::WriteProperty, ref->property(), a);
317 }
318
319 template<typename HeapObject>
320 static HeapObject *detached(HeapObject *ref)
321 {
322 if (ref->object() && !ref->enforcesLocation() && !readReference(ref))
323 return ref; // It's dead. No point in detaching it anymore
324
325 return ref->detached();
326 }
327};
328
329} // namespace QV4
330
331QT_END_NAMESPACE
332
333#endif // QV4REFERENCEOBJECT_P_H
DECLARE_HEAP_OBJECT(DateObject, ReferenceObject)
Definition qjsvalue.h:23
bool ReferenceObject::readReference< Heap::DateObject >(Heap::DateObject *ref)
Scoped< Object > ScopedObject
bool ReferenceObject::writeBack< Heap::DateObject >(Heap::DateObject *ref, int internalIndex)
DEFINE_OBJECT_VTABLE(DateObject)
DEFINE_OBJECT_VTABLE(DateCtor)
static bool InLeapYear(double t)
static const double msPerDay
static double DayFromYear(double y)
static QString ToString(double t, double localTZA)
static double UTC(double t, double localTZA)
static const double msPerSecond
static double ParseString(const QString &s, double localTZA)
static QString ToUTCString(double t)
static int msFromTime(double t)
static double DaysInYear(double y)
static QString ToLocaleTimeString(double t)
static const double msPerHour
static double YearFromTime(double t)
static QDateTime ToDateTime(double t, QTimeZone zone)
static QString ToTimeString(double t)
static double getLocalTZA()
static double TimeWithinDay(double t)
static double TimeFromYear(double y)
static QString ToLocaleDateString(double t)
static double DayFromMonth(double month, double leap)
static double WeekDay(double t)
static const double SecondsPerMinute
static double MakeDate(double day, double time)
static double MakeDay(double year, double month, double day)
static double DaylightSavingTA(double t, double localTZA)
static QString ToLocaleString(double t)
static double DayWithinYear(double t)
static const double HoursPerDay
static void addZeroPrefixedInt(QString &str, int num, int nDigits)
static double MonthFromTime(double t)
static int HourFromTime(double t)
static int SecFromTime(double t)
static double Day(double t)
static double TimeClip(double t)
static int MinFromTime(double t)
static QString ToDateString(double t)
static double MakeTime(double hour, double min, double sec, double ms)
static const double msPerMinute
static double DateFromTime(double t)
static double LocalTime(double t, double localTZA)
static const double MinutesPerHour
static double currentTime()
#define V4_NEEDS_DESTROY
#define Q_MANAGED_TYPE(type)
#define RETURN_RESULT(r)
static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int)
static QDate dateTimeToDate(const QDateTime &dateTime)
static QString dateTimeToString(const QDateTime &dateTime, ExecutionEngine *engine)
static QDateTime timestampToDateTime(double timestamp, QTimeZone zone=QTimeZone::LocalTime)
static QDateTime stringToDateTime(const QString &string, ExecutionEngine *engine)
static double dateTimeToNumber(const QDateTime &dateTime)
QString toString() const
static double componentsToTimestamp(double year, double month, double day, double hours, double mins, double secs, double ms, ExecutionEngine *v4)
static ReturnedValue method_setMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_UTC(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getTimezoneOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toLocaleDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toLocaleTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static void timezoneUpdated(ExecutionEngine *e)
static ReturnedValue method_setUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int)
static ReturnedValue method_getTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_parse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_now(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static double getThisDate(ExecutionEngine *v4, const Value *thisObject)
static ReturnedValue method_setSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
QDate toQDate() const
operator double() const
bool withReadonlyStoragePointer(Function function)
QVariant toVariant() const
bool withWriteonlyStoragePointer(Function function, ExecutionEngine *engine)
void init(QTime time, ExecutionEngine *engine)
static constexpr quint64 MaxDateVal
void init(QDate date)
void init(const QDateTime &dateTime)
Date & operator=(double value)
QTime toQTime() const
QDateTime toQDateTime() const
void init(double value)
void init(Date date)
void init(QV4::ExecutionEngine *engine)
ReferenceObjectEndpoint(ReferenceObject *reference)
An object that keeps track of the provenance of its owned value, allowing to reflect mutations on the...
static bool writeBack(HeapObject *ref, int internalIndex=AllProperties)
static bool readReference(HeapObject *ref)
static HeapObject * detached(HeapObject *ref)