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 QV4INTERNALCLASS_H
4#define QV4INTERNALCLASS_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 "qv4global_p.h"
18
19#include <QHash>
20#include <QVarLengthArray>
21#include <climits> // for UINT_MAX
22#include <private/qv4propertykey_p.h>
23#include <private/qv4heap_p.h>
24
25QT_BEGIN_NAMESPACE
26
27namespace QV4 {
28
29struct VTable;
30struct MarkStack;
31
32struct InternalClassEntry {
33 uint index;
34 uint setterIndex;
35 PropertyAttributes attributes;
36 bool isValid() const { return !attributes.isEmpty(); }
37};
38
39struct PropertyHashData;
40struct PropertyHash
41{
42 struct Entry {
43 PropertyKey identifier;
44 uint index;
45 uint setterIndex;
46 };
47
48 PropertyHashData *d;
49
50 inline PropertyHash();
51 inline PropertyHash(const PropertyHash &other);
52 inline ~PropertyHash();
53 PropertyHash &operator=(const PropertyHash &other);
54
55 void addEntry(const Entry &entry, int classSize);
56 Entry *lookup(PropertyKey identifier) const;
57 void detach(bool grow, int classSize);
58};
59
60struct PropertyHashData
61{
62 PropertyHashData(int numBits);
63 ~PropertyHashData() {
64 free(ptr: entries);
65 }
66
67 int refCount;
68 int alloc;
69 int size;
70 int numBits;
71 PropertyHash::Entry *entries;
72};
73
74inline PropertyHash::PropertyHash()
75{
76 d = new PropertyHashData(3);
77}
78
79inline PropertyHash::PropertyHash(const PropertyHash &other)
80{
81 d = other.d;
82 ++d->refCount;
83}
84
85inline PropertyHash::~PropertyHash()
86{
87 if (!--d->refCount)
88 delete d;
89}
90
91inline PropertyHash &PropertyHash::operator=(const PropertyHash &other)
92{
93 ++other.d->refCount;
94 if (!--d->refCount)
95 delete d;
96 d = other.d;
97 return *this;
98}
99
100
101
102inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const
103{
104 Q_ASSERT(d->entries);
105
106 uint idx = identifier.id() % d->alloc;
107 while (1) {
108 if (d->entries[idx].identifier == identifier)
109 return d->entries + idx;
110 if (!d->entries[idx].identifier.isValid())
111 return nullptr;
112 ++idx;
113 idx %= d->alloc;
114 }
115}
116
117template<class T>
118struct SharedInternalClassDataPrivate {};
119
120template<>
121struct SharedInternalClassDataPrivate<PropertyAttributes> {
122 SharedInternalClassDataPrivate(ExecutionEngine *engine)
123 : refcount(1),
124 m_alloc(0),
125 m_size(0),
126 m_data(nullptr),
127 m_engine(engine)
128 { }
129 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other);
130 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other,
131 uint pos, PropertyAttributes value);
132 ~SharedInternalClassDataPrivate();
133
134 void grow();
135
136 void markIfNecessary(const PropertyAttributes &) {}
137
138 uint alloc() const { return m_alloc; }
139 uint size() const { return m_size; }
140 void setSize(uint s) { m_size = s; }
141
142 PropertyAttributes at(uint i) const { Q_ASSERT(i < m_alloc); return data(i); }
143 void set(uint i, PropertyAttributes t) { Q_ASSERT(i < m_alloc); setData(i, t); }
144
145 void mark(MarkStack *) {}
146
147 int refcount = 1;
148private:
149 uint m_alloc;
150 uint m_size;
151
152 enum {
153 SizeOfAttributesPointer = sizeof(PropertyAttributes *),
154 SizeOfAttributes = sizeof(PropertyAttributes),
155 NumAttributesInPointer = SizeOfAttributesPointer / SizeOfAttributes,
156 };
157
158 static_assert(NumAttributesInPointer > 0);
159
160 PropertyAttributes data(uint i) const {
161 return m_alloc > NumAttributesInPointer ? m_data[i] : m_inlineData[i];
162 }
163
164 void setData(uint i, PropertyAttributes t) {
165 if (m_alloc > NumAttributesInPointer)
166 m_data[i] = t;
167 else
168 m_inlineData[i] = t;
169 }
170
171 union {
172 PropertyAttributes *m_data;
173 PropertyAttributes m_inlineData[NumAttributesInPointer];
174 };
175 ExecutionEngine *m_engine;
176};
177
178template<>
179struct SharedInternalClassDataPrivate<PropertyKey> {
180 SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e) {}
181 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other);
182 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value);
183 ~SharedInternalClassDataPrivate() {}
184
185 template<typename StringOrSymbol = Heap::StringOrSymbol>
186 void markIfNecessary(const PropertyKey &value);
187
188 void grow();
189 uint alloc() const;
190 uint size() const;
191 void setSize(uint s);
192
193 PropertyKey at(uint i) const;
194 void set(uint i, PropertyKey t);
195
196 void mark(MarkStack *s);
197
198 int refcount = 1;
199private:
200 ExecutionEngine *engine;
201 WriteBarrier::Pointer<Heap::MemberData> data;
202};
203
204template<typename StringOrSymbol>
205void QV4::SharedInternalClassDataPrivate<PropertyKey>::markIfNecessary(const PropertyKey &value)
206{
207 QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) {
208 if constexpr (QV4::WriteBarrier::isInsertionBarrier) {
209 if (auto s = value.asStringOrSymbol<StringOrSymbol>())
210 s->mark(stack);
211 }
212 });
213}
214
215template <typename T>
216struct SharedInternalClassData {
217 using Private = SharedInternalClassDataPrivate<T>;
218 Private *d;
219
220 inline SharedInternalClassData(ExecutionEngine *e) {
221 d = new Private(e);
222 }
223
224 inline SharedInternalClassData(const SharedInternalClassData &other)
225 : d(other.d)
226 {
227 ++d->refcount;
228 }
229 inline ~SharedInternalClassData() {
230 if (!--d->refcount)
231 delete d;
232 }
233 SharedInternalClassData &operator=(const SharedInternalClassData &other) {
234 ++other.d->refcount;
235 if (!--d->refcount)
236 delete d;
237 d = other.d;
238 return *this;
239 }
240
241 void add(uint pos, T value) {
242 d->markIfNecessary(value);
243 if (pos < d->size()) {
244 Q_ASSERT(d->refcount > 1);
245 // need to detach
246 Private *dd = new Private(*d, pos, value);
247 --d->refcount;
248 d = dd;
249 return;
250 }
251 Q_ASSERT(pos == d->size());
252 if (pos == d->alloc())
253 d->grow();
254 if (pos >= d->alloc()) {
255 qBadAlloc();
256 } else {
257 d->setSize(d->size() + 1);
258 d->set(pos, value);
259 }
260 }
261
262 void set(uint pos, T value) {
263 Q_ASSERT(pos < d->size());
264 d->markIfNecessary(value);
265 if (d->refcount > 1) {
266 // need to detach
267 Private *dd = new Private(*d);
268 --d->refcount;
269 d = dd;
270 }
271 d->set(pos, value);
272 }
273
274 T at(uint i) const {
275 Q_ASSERT(i < d->size());
276 return d->at(i);
277 }
278 T operator[] (uint i) {
279 Q_ASSERT(i < d->size());
280 return d->at(i);
281 }
282
283 void mark(MarkStack *s) { d->mark(s); }
284};
285
286struct InternalClassTransition
287{
288 union {
289 PropertyKey id;
290 const VTable *vtable;
291 Heap::Object *prototype;
292 };
293 Heap::InternalClass *lookup;
294 int flags;
295 enum {
296 // range 0-0xff is reserved for attribute changes
297 StructureChange = 0x100,
298 NotExtensible = StructureChange | (1 << 0),
299 VTableChange = StructureChange | (1 << 1),
300 PrototypeChange = StructureChange | (1 << 2),
301 ProtoClass = StructureChange | (1 << 3),
302 Sealed = StructureChange | (1 << 4),
303 Frozen = StructureChange | (1 << 5),
304 Locked = StructureChange | (1 << 6),
305 };
306
307 bool operator==(const InternalClassTransition &other) const
308 { return id == other.id && flags == other.flags; }
309
310 bool operator<(const InternalClassTransition &other) const
311 { return flags < other.flags || (flags == other.flags && id < other.id); }
312};
313
314namespace Heap {
315
316struct InternalClass : Base {
317 enum Flag {
318 NotExtensible = 1 << 0,
319 Sealed = 1 << 1,
320 Frozen = 1 << 2,
321 UsedAsProto = 1 << 3,
322 Locked = 1 << 4,
323 };
324 enum { MaxRedundantTransitions = 255 };
325
326 ExecutionEngine *engine;
327 const VTable *vtable;
328 quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
329 Heap::Object *prototype;
330 InternalClass *parent;
331
332 PropertyHash propertyTable; // id to valueIndex
333 SharedInternalClassData<PropertyKey> nameMap;
334 SharedInternalClassData<PropertyAttributes> propertyData;
335
336 typedef InternalClassTransition Transition;
337 QVarLengthArray<Transition, 1> transitions;
338 InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
339
340 uint size;
341 quint8 numRedundantTransitions;
342 quint8 flags;
343
344 bool isExtensible() const { return !(flags & NotExtensible); }
345 bool isSealed() const { return flags & Sealed; }
346 bool isFrozen() const { return flags & Frozen; }
347 bool isUsedAsProto() const { return flags & UsedAsProto; }
348 bool isLocked() const { return flags & Locked; }
349
350 void init(ExecutionEngine *engine);
351 void init(InternalClass *other);
352 void destroy();
353
354 Q_QML_EXPORT ReturnedValue keyAt(uint index) const;
355 Q_REQUIRED_RESULT InternalClass *nonExtensible();
356 Q_REQUIRED_RESULT InternalClass *locked();
357
358 static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry);
359 Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
360 Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
361 static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry = nullptr);
362 static void removeMember(QV4::Object *object, PropertyKey identifier);
363 PropertyHash::Entry *findEntry(const PropertyKey id)
364 {
365 Q_ASSERT(id.isStringOrSymbol());
366
367 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
368 if (e && e->index < size)
369 return e;
370
371 return nullptr;
372 }
373
374 InternalClassEntry find(const PropertyKey id)
375 {
376 Q_ASSERT(id.isStringOrSymbol());
377
378 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
379 if (e && e->index < size) {
380 PropertyAttributes a = propertyData.at(i: e->index);
381 if (!a.isEmpty())
382 return { .index: e->index, .setterIndex: e->setterIndex, .attributes: a };
383 }
384
385 return { UINT_MAX, UINT_MAX, .attributes: Attr_Invalid };
386 }
387
388 struct IndexAndAttribute {
389 uint index;
390 PropertyAttributes attrs;
391 bool isValid() const { return index != UINT_MAX; }
392 };
393
394 IndexAndAttribute findValueOrGetter(const PropertyKey id)
395 {
396 Q_ASSERT(id.isStringOrSymbol());
397
398 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
399 if (e && e->index < size) {
400 PropertyAttributes a = propertyData.at(i: e->index);
401 if (!a.isEmpty())
402 return { .index: e->index, .attrs: a };
403 }
404
405 return { UINT_MAX, .attrs: Attr_Invalid };
406 }
407
408 IndexAndAttribute findValueOrSetter(const PropertyKey id)
409 {
410 Q_ASSERT(id.isStringOrSymbol());
411
412 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
413 if (e && e->index < size) {
414 PropertyAttributes a = propertyData.at(i: e->index);
415 if (!a.isEmpty()) {
416 if (a.isAccessor()) {
417 Q_ASSERT(e->setterIndex != UINT_MAX);
418 return { .index: e->setterIndex, .attrs: a };
419 }
420 return { .index: e->index, .attrs: a };
421 }
422 }
423
424 return { UINT_MAX, .attrs: Attr_Invalid };
425 }
426
427 uint indexOfValueOrGetter(const PropertyKey id)
428 {
429 Q_ASSERT(id.isStringOrSymbol());
430
431 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
432 if (e && e->index < size) {
433 Q_ASSERT(!propertyData.at(e->index).isEmpty());
434 return e->index;
435 }
436
437 return UINT_MAX;
438 }
439
440 bool verifyIndex(const PropertyKey id, uint index)
441 {
442 Q_ASSERT(id.isStringOrSymbol());
443
444 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
445 if (e && e->index < size) {
446 Q_ASSERT(!propertyData.at(e->index).isEmpty());
447 return e->index == index;
448 }
449
450 return false;
451 }
452
453 Q_REQUIRED_RESULT InternalClass *sealed();
454 Q_REQUIRED_RESULT InternalClass *frozen();
455 Q_REQUIRED_RESULT InternalClass *canned(); // sealed + nonExtensible
456 Q_REQUIRED_RESULT InternalClass *cryopreserved(); // frozen + sealed + nonExtensible
457 bool isImplicitlyFrozen() const;
458
459 Q_REQUIRED_RESULT InternalClass *asProtoClass();
460
461 Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
462 if (vtable == vt)
463 return this;
464 return changeVTableImpl(vt);
465 }
466 Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
467 if (prototype == proto)
468 return this;
469 return changePrototypeImpl(proto);
470 }
471
472 void updateProtoUsage(Heap::Object *o);
473
474 static void markObjects(Heap::Base *ic, MarkStack *stack);
475
476private:
477 Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
478 Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
479 InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry);
480
481 void removeChildEntry(InternalClass *child);
482 friend struct ::QV4::ExecutionEngine;
483};
484
485inline
486void Base::markObjects(Base *b, MarkStack *stack)
487{
488 b->internalClass->mark(markStack: stack);
489}
490
491}
492
493}
494
495QT_END_NAMESPACE
496
497#endif
498

Provided by KDAB

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

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