1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#ifndef QV4INTERNALCLASS_H
40#define QV4INTERNALCLASS_H
41
42//
43// W A R N I N G
44// -------------
45//
46// This file is not part of the Qt API. It exists purely as an
47// implementation detail. This header file may change from version to
48// version without notice, or even be removed.
49//
50// We mean it.
51//
52
53#include "qv4global_p.h"
54
55#include <QHash>
56#include <private/qv4propertykey_p.h>
57#include <private/qv4heap_p.h>
58
59QT_BEGIN_NAMESPACE
60
61namespace QV4 {
62
63struct VTable;
64struct MarkStack;
65
66struct InternalClassEntry {
67 uint index;
68 uint setterIndex;
69 PropertyAttributes attributes;
70 bool isValid() const { return !attributes.isEmpty(); }
71};
72
73struct PropertyHashData;
74struct PropertyHash
75{
76 struct Entry {
77 PropertyKey identifier;
78 uint index;
79 uint setterIndex;
80 };
81
82 PropertyHashData *d;
83
84 inline PropertyHash();
85 inline PropertyHash(const PropertyHash &other);
86 inline ~PropertyHash();
87 PropertyHash &operator=(const PropertyHash &other);
88
89 void addEntry(const Entry &entry, int classSize);
90 Entry *lookup(PropertyKey identifier) const;
91 int removeIdentifier(PropertyKey identifier, int classSize);
92 void detach(bool grow, int classSize);
93};
94
95struct PropertyHashData
96{
97 PropertyHashData(int numBits);
98 ~PropertyHashData() {
99 free(ptr: entries);
100 }
101
102 int refCount;
103 int alloc;
104 int size;
105 int numBits;
106 PropertyHash::Entry *entries;
107};
108
109inline PropertyHash::PropertyHash()
110{
111 d = new PropertyHashData(3);
112}
113
114inline PropertyHash::PropertyHash(const PropertyHash &other)
115{
116 d = other.d;
117 ++d->refCount;
118}
119
120inline PropertyHash::~PropertyHash()
121{
122 if (!--d->refCount)
123 delete d;
124}
125
126inline PropertyHash &PropertyHash::operator=(const PropertyHash &other)
127{
128 ++other.d->refCount;
129 if (!--d->refCount)
130 delete d;
131 d = other.d;
132 return *this;
133}
134
135
136
137inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const
138{
139 Q_ASSERT(d->entries);
140
141 uint idx = identifier.id() % d->alloc;
142 while (1) {
143 if (d->entries[idx].identifier == identifier)
144 return d->entries + idx;
145 if (!d->entries[idx].identifier.isValid())
146 return nullptr;
147 ++idx;
148 idx %= d->alloc;
149 }
150}
151
152template<class T>
153struct SharedInternalClassDataPrivate {};
154
155template<>
156struct SharedInternalClassDataPrivate<PropertyAttributes> {
157 SharedInternalClassDataPrivate(ExecutionEngine *engine)
158 : refcount(1),
159 m_alloc(0),
160 m_size(0),
161 data(nullptr),
162 m_engine(engine)
163 { }
164 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other);
165 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other,
166 uint pos, PropertyAttributes value);
167 ~SharedInternalClassDataPrivate();
168
169 void grow();
170
171 uint alloc() const { return m_alloc; }
172 uint size() const { return m_size; }
173 void setSize(uint s) { m_size = s; }
174
175 PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
176 void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
177
178 void mark(MarkStack *) {}
179
180 int refcount = 1;
181private:
182 uint m_alloc;
183 uint m_size;
184 PropertyAttributes *data;
185 ExecutionEngine *m_engine;
186};
187
188template<>
189struct SharedInternalClassDataPrivate<PropertyKey> {
190 SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e), data(nullptr) {}
191 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other);
192 SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value);
193 ~SharedInternalClassDataPrivate() {}
194
195 void grow();
196 uint alloc() const;
197 uint size() const;
198 void setSize(uint s);
199
200 PropertyKey at(uint i);
201 void set(uint i, PropertyKey t);
202
203 void mark(MarkStack *s);
204
205 int refcount = 1;
206private:
207 ExecutionEngine *engine;
208 Heap::MemberData *data;
209};
210
211template <typename T>
212struct SharedInternalClassData {
213 using Private = SharedInternalClassDataPrivate<T>;
214 Private *d;
215
216 inline SharedInternalClassData(ExecutionEngine *e) {
217 d = new Private(e);
218 }
219
220 inline SharedInternalClassData(const SharedInternalClassData &other)
221 : d(other.d)
222 {
223 ++d->refcount;
224 }
225 inline ~SharedInternalClassData() {
226 if (!--d->refcount)
227 delete d;
228 }
229 SharedInternalClassData &operator=(const SharedInternalClassData &other) {
230 ++other.d->refcount;
231 if (!--d->refcount)
232 delete d;
233 d = other.d;
234 return *this;
235 }
236
237 void add(uint pos, T value) {
238 if (pos < d->size()) {
239 Q_ASSERT(d->refcount > 1);
240 // need to detach
241 Private *dd = new Private(*d, pos, value);
242 if (!--d->refcount)
243 delete d;
244 d = dd;
245 return;
246 }
247 Q_ASSERT(pos == d->size());
248 if (pos == d->alloc())
249 d->grow();
250 if (pos >= d->alloc()) {
251 qBadAlloc();
252 } else {
253 d->setSize(d->size() + 1);
254 d->set(pos, value);
255 }
256 }
257
258 void set(uint pos, T value) {
259 Q_ASSERT(pos < d->size());
260 if (d->refcount > 1) {
261 // need to detach
262 Private *dd = new Private(*d);
263 if (!--d->refcount)
264 delete d;
265 d = dd;
266 }
267 d->set(pos, value);
268 }
269
270 T at(uint i) const {
271 Q_ASSERT(i < d->size());
272 return d->at(i);
273 }
274 T operator[] (uint i) {
275 Q_ASSERT(i < d->size());
276 return d->at(i);
277 }
278
279 void mark(MarkStack *s) { d->mark(s); }
280};
281
282struct InternalClassTransition
283{
284 union {
285 PropertyKey id;
286 const VTable *vtable;
287 Heap::Object *prototype;
288 };
289 Heap::InternalClass *lookup;
290 int flags;
291 enum {
292 // range 0-0xff is reserved for attribute changes
293 NotExtensible = 0x100,
294 VTableChange = 0x200,
295 PrototypeChange = 0x201,
296 ProtoClass = 0x202,
297 Sealed = 0x203,
298 Frozen = 0x204
299 };
300
301 bool operator==(const InternalClassTransition &other) const
302 { return id == other.id && flags == other.flags; }
303
304 bool operator<(const InternalClassTransition &other) const
305 { return id < other.id || (id == other.id && flags < other.flags); }
306};
307
308namespace Heap {
309
310struct InternalClass : Base {
311 ExecutionEngine *engine;
312 const VTable *vtable;
313 quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
314 Heap::Object *prototype;
315 InternalClass *parent;
316
317 PropertyHash propertyTable; // id to valueIndex
318 SharedInternalClassData<PropertyKey> nameMap;
319 SharedInternalClassData<PropertyAttributes> propertyData;
320
321 typedef InternalClassTransition Transition;
322 std::vector<Transition> transitions;
323 InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
324
325 uint size;
326 bool extensible;
327 bool isSealed;
328 bool isFrozen;
329 bool isUsedAsProto;
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
338 static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry);
339 Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
340 Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
341 static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry = nullptr);
342 static void removeMember(QV4::Object *object, PropertyKey identifier);
343 PropertyHash::Entry *findEntry(const PropertyKey id)
344 {
345 Q_ASSERT(id.isStringOrSymbol());
346
347 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
348 if (e && e->index < size)
349 return e;
350
351 return nullptr;
352 }
353
354 InternalClassEntry find(const PropertyKey id)
355 {
356 Q_ASSERT(id.isStringOrSymbol());
357
358 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
359 if (e && e->index < size) {
360 PropertyAttributes a = propertyData.at(i: e->index);
361 if (!a.isEmpty())
362 return { .index: e->index, .setterIndex: e->setterIndex, .attributes: a };
363 }
364
365 return { UINT_MAX, UINT_MAX, .attributes: Attr_Invalid };
366 }
367
368 struct IndexAndAttribute {
369 uint index;
370 PropertyAttributes attrs;
371 bool isValid() const { return index != UINT_MAX; }
372 };
373
374 IndexAndAttribute findValueOrGetter(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, .attrs: a };
383 }
384
385 return { UINT_MAX, .attrs: Attr_Invalid };
386 }
387
388 IndexAndAttribute findValueOrSetter(const PropertyKey id)
389 {
390 Q_ASSERT(id.isStringOrSymbol());
391
392 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
393 if (e && e->index < size) {
394 PropertyAttributes a = propertyData.at(i: e->index);
395 if (!a.isEmpty()) {
396 if (a.isAccessor()) {
397 Q_ASSERT(e->setterIndex != UINT_MAX);
398 return { .index: e->setterIndex, .attrs: a };
399 }
400 return { .index: e->index, .attrs: a };
401 }
402 }
403
404 return { UINT_MAX, .attrs: Attr_Invalid };
405 }
406
407 uint indexOfValueOrGetter(const PropertyKey id)
408 {
409 Q_ASSERT(id.isStringOrSymbol());
410
411 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
412 if (e && e->index < size) {
413 Q_ASSERT(!propertyData.at(e->index).isEmpty());
414 return e->index;
415 }
416
417 return UINT_MAX;
418 }
419
420 bool verifyIndex(const PropertyKey id, uint index)
421 {
422 Q_ASSERT(id.isStringOrSymbol());
423
424 PropertyHash::Entry *e = propertyTable.lookup(identifier: id);
425 if (e && e->index < size) {
426 Q_ASSERT(!propertyData.at(e->index).isEmpty());
427 return e->index == index;
428 }
429
430 return false;
431 }
432
433 Q_REQUIRED_RESULT InternalClass *sealed();
434 Q_REQUIRED_RESULT InternalClass *frozen();
435 Q_REQUIRED_RESULT InternalClass *canned(); // sealed + nonExtensible
436 Q_REQUIRED_RESULT InternalClass *cryopreserved(); // frozen + sealed + nonExtensible
437 bool isImplicitlyFrozen() const;
438
439 Q_REQUIRED_RESULT InternalClass *asProtoClass();
440
441 Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
442 if (vtable == vt)
443 return this;
444 return changeVTableImpl(vt);
445 }
446 Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
447 if (prototype == proto)
448 return this;
449 return changePrototypeImpl(proto);
450 }
451
452 void updateProtoUsage(Heap::Object *o);
453
454 static void markObjects(Heap::Base *ic, MarkStack *stack);
455
456private:
457 Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
458 Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
459 InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry);
460
461 void removeChildEntry(InternalClass *child);
462 friend struct ::QV4::ExecutionEngine;
463};
464
465inline
466void Base::markObjects(Base *b, MarkStack *stack)
467{
468 b->internalClass->mark(markStack: stack);
469}
470
471}
472
473}
474
475QT_END_NAMESPACE
476
477#endif
478

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