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 | |
25 | QT_BEGIN_NAMESPACE |
26 | |
27 | namespace QV4 { |
28 | |
29 | struct VTable; |
30 | struct MarkStack; |
31 | |
32 | struct InternalClassEntry { |
33 | uint index; |
34 | uint setterIndex; |
35 | PropertyAttributes attributes; |
36 | bool isValid() const { return !attributes.isEmpty(); } |
37 | }; |
38 | |
39 | struct PropertyHashData; |
40 | struct 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 | |
60 | struct 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 | |
74 | inline PropertyHash::PropertyHash() |
75 | { |
76 | d = new PropertyHashData(3); |
77 | } |
78 | |
79 | inline PropertyHash::PropertyHash(const PropertyHash &other) |
80 | { |
81 | d = other.d; |
82 | ++d->refCount; |
83 | } |
84 | |
85 | inline PropertyHash::~PropertyHash() |
86 | { |
87 | if (!--d->refCount) |
88 | delete d; |
89 | } |
90 | |
91 | inline 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 | |
102 | inline 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 | |
117 | template<class T> |
118 | struct SharedInternalClassDataPrivate {}; |
119 | |
120 | template<> |
121 | struct 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; |
148 | private: |
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 | |
178 | template<> |
179 | struct 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; |
199 | private: |
200 | ExecutionEngine *engine; |
201 | WriteBarrier::Pointer<Heap::MemberData> data; |
202 | }; |
203 | |
204 | template<typename StringOrSymbol> |
205 | void 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 | |
215 | template <typename T> |
216 | struct 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 | |
286 | struct 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 | |
314 | namespace Heap { |
315 | |
316 | struct 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 | |
476 | private: |
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 | |
485 | inline |
486 | void Base::markObjects(Base *b, MarkStack *stack) |
487 | { |
488 | b->internalClass->mark(markStack: stack); |
489 | } |
490 | |
491 | } |
492 | |
493 | } |
494 | |
495 | QT_END_NAMESPACE |
496 | |
497 | #endif |
498 |
Definitions
- InternalClassEntry
- isValid
- PropertyHash
- Entry
- PropertyHashData
- ~PropertyHashData
- PropertyHash
- PropertyHash
- ~PropertyHash
- operator=
- lookup
- SharedInternalClassDataPrivate
- SharedInternalClassDataPrivate
- SharedInternalClassDataPrivate
- markIfNecessary
- alloc
- size
- setSize
- at
- set
- mark
- data
- setData
- SharedInternalClassDataPrivate
- SharedInternalClassDataPrivate
- ~SharedInternalClassDataPrivate
- markIfNecessary
- SharedInternalClassData
- SharedInternalClassData
- SharedInternalClassData
- ~SharedInternalClassData
- operator=
- add
- set
- at
- operator[]
- mark
- InternalClassTransition
- operator==
- operator<
- InternalClass
- Flag
- isExtensible
- isSealed
- isFrozen
- isUsedAsProto
- isLocked
- findEntry
- find
- IndexAndAttribute
- isValid
- findValueOrGetter
- findValueOrSetter
- indexOfValueOrGetter
- verifyIndex
- changeVTable
- changePrototype
Start learning QML with our Intro Training
Find out more