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

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