Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qv4internalclass.cpp
Go to the documentation of this file.
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
5#include <qv4string_p.h>
6#include <qv4engine_p.h>
8#include "qv4object_p.h"
9#include "qv4value_p.h"
10#include "qv4mm_p.h"
11#include <private/qprimefornumbits_p.h>
12
14
15namespace QV4 {
16
18 : refCount(1)
19 , size(0)
20 , numBits(numBits)
21{
24 memset(entries, 0, alloc*sizeof(PropertyHash::Entry));
25}
26
28{
29 // fill up to max 50%
30 bool grow = (d->alloc <= d->size*2);
31
32 if (classSize < d->size || grow)
33 detach(grow, classSize);
34
35 uint idx = entry.identifier.id() % d->alloc;
36 while (d->entries[idx].identifier.isValid()) {
37 ++idx;
38 idx %= d->alloc;
39 }
40 d->entries[idx] = entry;
41 ++d->size;
42}
43
44void PropertyHash::detach(bool grow, int classSize)
45{
46 if (d->refCount == 1 && !grow)
47 return;
48
50 for (int i = 0; i < d->alloc; ++i) {
51 const Entry &e = d->entries[i];
52 if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
53 continue;
54 uint idx = e.identifier.id() % dd->alloc;
55 while (dd->entries[idx].identifier.isValid()) {
56 ++idx;
57 idx %= dd->alloc;
58 }
59 dd->entries[idx] = e;
60 }
61 dd->size = classSize;
62 if (!--d->refCount)
63 delete d;
64 d = dd;
65}
66
67
69 : refcount(1),
72{
73 if (other.alloc()) {
74 const uint s = other.size();
75 data = MemberData::allocate(engine, other.alloc(), other.data);
76 setSize(s);
77 }
78}
79
80SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other,
81 uint pos, PropertyKey value)
82 : refcount(1),
84{
85 data = MemberData::allocate(engine, other.alloc(), nullptr);
86 memcpy(data, other.data, sizeof(Heap::MemberData) - sizeof(Value) + pos*sizeof(Value));
87 data->values.size = pos + 1;
88 data->values.set(engine, pos, Value::fromReturnedValue(value.id()));
89}
90
92{
93 const uint a = alloc() * 2;
94 const uint s = size();
96 setSize(s);
97 Q_ASSERT(alloc() >= a);
98}
99
101{
102 return data ? data->values.alloc : 0;
103}
104
106{
107 return data ? data->values.size : 0;
108}
109
111{
112 Q_ASSERT(data && s <= alloc());
113 data->values.size = s;
114}
115
117{
118 Q_ASSERT(data && i < size());
119 return PropertyKey::fromId(data->values.values[i].rawValue());
120}
121
123{
124 Q_ASSERT(data && i < size());
125 data->values.values[i].rawValueRef() = t.id();
126}
127
129{
130 if (data)
131 data->mark(s);
132}
133
137 : refcount(1),
138 m_alloc(qMin(other.m_alloc, pos + 8)),
139 m_size(pos + 1),
140 m_engine(other.m_engine)
141{
142 Q_ASSERT(m_size <= m_alloc);
143 Q_ASSERT(m_alloc > 0);
144
145 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
146 const PropertyAttributes *source = other.m_alloc > NumAttributesInPointer
147 ? other.m_data
148 : other.m_inlineData;
150 if (m_alloc > NumAttributesInPointer)
151 m_data = target = new PropertyAttributes[m_alloc];
152 else
153 target = m_inlineData;
154
155 memcpy(target, source, (m_size - 1) * sizeof(PropertyAttributes));
156 target[pos] = value;
157}
158
161 : refcount(1),
162 m_alloc(other.m_alloc),
163 m_size(other.m_size),
164 m_engine(other.m_engine)
165{
166 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
167 if (m_alloc > NumAttributesInPointer) {
168 m_data = new PropertyAttributes[m_alloc];
169 memcpy(m_data, other.m_data, m_size*sizeof(PropertyAttributes));
170 } else if (m_alloc > 0) {
171 memcpy(m_inlineData, other.m_inlineData, m_alloc * sizeof(PropertyAttributes));
172 } else {
173 m_data = nullptr;
174 }
175}
176
178{
179 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
180 -qptrdiff(m_alloc * sizeof(PropertyAttributes)));
181 if (m_alloc > NumAttributesInPointer)
182 delete [] m_data;
183}
184
186 uint alloc;
187 if (!m_alloc) {
188 alloc = NumAttributesInPointer;
189 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(alloc * sizeof(PropertyAttributes));
190 } else {
191 // yes, signed. We don't want to deal with stuff > 2G
192 const uint currentSize = m_alloc * sizeof(PropertyAttributes);
193 if (currentSize < uint(std::numeric_limits<int>::max() / 2))
194 alloc = m_alloc * 2;
195 else
196 alloc = std::numeric_limits<int>::max() / sizeof(PropertyAttributes);
197
198 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
199 (alloc - m_alloc) * sizeof(PropertyAttributes));
200 }
201
202 if (alloc > NumAttributesInPointer) {
203 auto *n = new PropertyAttributes[alloc];
204 if (m_alloc > NumAttributesInPointer) {
205 memcpy(n, m_data, m_alloc * sizeof(PropertyAttributes));
206 delete [] m_data;
207 } else if (m_alloc > 0) {
208 memcpy(n, m_inlineData, m_alloc * sizeof(PropertyAttributes));
209 }
210 m_data = n;
211 }
212 m_alloc = alloc;
213}
214
215namespace Heap {
216
218{
219// InternalClass is automatically zeroed during allocation:
220// prototype = nullptr;
221// parent = nullptr;
222// size = 0;
223// numRedundantTransitions = 0;
224// flags = 0;
225
226 Base::init();
230 new (&transitions) std::vector<Transition>();
231
232 this->engine = engine;
233 vtable = QV4::InternalClass::staticVTable();
235
236 // Also internal classes need an internal class pointer. Simply make it point to itself
237 internalClass.set(engine, this);
238}
239
240
242{
243 Base::init();
244 new (&propertyTable) PropertyHash(other->propertyTable);
247 new (&transitions) std::vector<Transition>();
248
249 engine = other->engine;
250 vtable = other->vtable;
251 prototype = other->prototype;
252 parent = other;
253 size = other->size;
254 numRedundantTransitions = other->numRedundantTransitions;
255 flags = other->flags;
257
258 internalClass.set(engine, other->internalClass);
259}
260
262{
263 for (const auto &t : transitions) {
264 if (t.lookup) {
265#ifndef QT_NO_DEBUG
266 Q_ASSERT(t.lookup->parent == this);
267#endif
268 t.lookup->parent = nullptr;
269 }
270 }
271
272 if (parent && parent->engine && parent->isMarked())
273 parent->removeChildEntry(this);
274
276 nameMap.~SharedInternalClassData<PropertyKey>();
277 propertyData.~SharedInternalClassData<PropertyAttributes>();
278 transitions.~vector<Transition>();
279 engine = nullptr;
281}
282
284{
285 return nameMap.at(index).toQString();
286}
287
289{
290 Q_ASSERT(id.isStringOrSymbol());
291
292 Heap::InternalClass *oldClass = object->internalClass();
293 Heap::InternalClass *newClass = oldClass->changeMember(id, data, entry);
294 object->setInternalClass(newClass);
295}
296
298{
299 std::vector<Transition>::iterator it = std::lower_bound(transitions.begin(), transitions.end(), t);
300 if (it != transitions.end() && *it == t) {
301 return *it;
302 } else {
303 it = transitions.insert(it, t);
304 return *it;
305 }
306}
307
309{
310 // add a dummy entry, since we need two entries for accessors
311 newClass->propertyTable.addEntry(e, newClass->size);
312 newClass->nameMap.add(newClass->size, PropertyKey::invalid());
313 newClass->propertyData.add(newClass->size, PropertyAttributes());
314 ++newClass->size;
315}
316
318{
319 PropertyAttributes attributes;
320 attributes.m_all = uchar(flags);
321 return attributes;
322}
323
325{
327 return orig;
328
329 // We will generally add quite a few transitions here. We have 255 redundant ones.
330 // We can expect at least as many significant ones in addition.
331 std::vector<InternalClassTransition> transitions;
332
333 Scope scope(orig->engine);
335
336 {
337 quint8 remainingRedundantTransitions = orig->numRedundantTransitions;
339 int structureChanges = 0;
340
342 while (parent && remainingRedundantTransitions > 0) {
344 const auto it = std::find_if(
345 parent->d()->transitions.begin(), parent->d()->transitions.end(),
346 [&child](const InternalClassTransition &t) {
347 return child->d() == t.lookup;
348 });
349 Q_ASSERT(it != parent->d()->transitions.end());
350
352 // A structural change. Each kind of structural change has to be recorded only once.
353 if ((structureChanges & it->flags) != it->flags) {
354 transitions.push_back(*it);
355 structureChanges |= it->flags;
356 } else {
357 --remainingRedundantTransitions;
358 }
359 } else if (!properties.contains(it->id)) {
360 // We only need the final state of the property.
361 properties.insert(it->id);
362
363 // Property removal creates _two_ redundant transitions.
364 // We don't have to replay either, but numRedundantTransitions only records one.
365 if (it->flags != 0)
366 transitions.push_back(*it);
367 } else {
368 --remainingRedundantTransitions;
369 }
370
371 child = parent->d();
372 parent = child->d()->parent;
373 Q_ASSERT(child->d() != parent->d());
374 }
375 }
376
377 for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) {
378 switch (it->flags) {
380 child = child->d()->nonExtensible();
381 continue;
383 child = child->d()->changeVTable(it->vtable);
384 continue;
386 child = child->d()->changePrototype(it->prototype);
387 continue;
389 child = child->d()->asProtoClass();
390 continue;
392 child = child->d()->sealed();
393 continue;
395 child = child->d()->frozen();
396 continue;
398 child = child->d()->locked();
399 continue;
400 default:
401 Q_ASSERT(it->flags != 0);
403 child = child->addMember(it->id, attributesFromFlags(it->flags));
404 continue;
405 }
406 }
407
408 return child->d();
409}
410
413{
414 if (!data.isEmpty())
415 data.resolve();
416 PropertyHash::Entry *e = findEntry(identifier);
417 Q_ASSERT(e && e->index != UINT_MAX);
418 uint idx = e->index;
419 Q_ASSERT(idx != UINT_MAX);
420
421 if (entry) {
422 entry->index = idx;
423 entry->setterIndex = e->setterIndex;
424 entry->attributes = data;
425 }
426
427 if (data == propertyData.at(idx))
428 return this;
429
430 Transition temp = { { identifier }, nullptr, int(data.all()) };
432 if (t.lookup)
433 return t.lookup;
434
435 // create a new class and add it to the tree
436 Heap::InternalClass *newClass = engine->newClass(this);
437 if (data.isAccessor() && e->setterIndex == UINT_MAX) {
438 Q_ASSERT(!propertyData.at(idx).isAccessor());
439
440 // add a dummy entry for the accessor
441 if (entry)
442 entry->setterIndex = newClass->size;
443 e->setterIndex = newClass->size;
444 addDummyEntry(newClass, *e);
445 }
446
447 newClass->propertyData.set(idx, data);
448
449 t.lookup = newClass;
450 Q_ASSERT(t.lookup);
451
452 return cleanInternalClass(newClass);
453}
454
455Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
456{
457 Scope scope(engine);
458 ScopedValue protectThis(scope, this);
459 if (proto)
460 proto->setUsedAsProto();
461 Q_ASSERT(prototype != proto);
462 Q_ASSERT(!proto || proto->internalClass->isUsedAsProto());
463
465 temp.prototype = proto;
466
468 if (t.lookup)
469 return t.lookup;
470
471 // create a new class and add it to the tree
472 Heap::InternalClass *newClass = engine->newClass(this);
473 newClass->prototype = proto;
474
475 t.lookup = newClass;
476 return prototype ? cleanInternalClass(newClass) : newClass;
477}
478
479Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
480{
481 Q_ASSERT(vtable != vt);
482
484 temp.vtable = vt;
485
487 if (t.lookup)
488 return t.lookup;
489
490 // create a new class and add it to the tree
491 Heap::InternalClass *newClass = engine->newClass(this);
492 newClass->vtable = vt;
493
494 t.lookup = newClass;
495 Q_ASSERT(t.lookup);
496 Q_ASSERT(newClass->vtable);
497 return vtable == QV4::InternalClass::staticVTable()
498 ? newClass
499 : cleanInternalClass(newClass);
500}
501
503{
504 if (!isExtensible())
505 return this;
506
509 if (t.lookup)
510 return t.lookup;
511
512 Heap::InternalClass *newClass = engine->newClass(this);
513 newClass->flags |= NotExtensible;
514
515 t.lookup = newClass;
516 Q_ASSERT(t.lookup);
517 return newClass;
518}
519
521{
522 if (isLocked())
523 return this;
524
525 Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::Locked};
527 if (t.lookup)
528 return t.lookup;
529
530 Heap::InternalClass *newClass = engine->newClass(this);
531 newClass->flags |= Locked;
532
533 t.lookup = newClass;
534 Q_ASSERT(t.lookup);
535 return newClass;
536}
537
539{
540 Q_ASSERT(id.isStringOrSymbol());
541 if (!data.isEmpty())
542 data.resolve();
543 PropertyHash::Entry *e = object->internalClass()->findEntry(id);
544 if (e) {
545 changeMember(object, id, data, entry);
546 return;
547 }
548
549 Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, entry);
550 object->setInternalClass(newClass);
551}
552
554{
555 Q_ASSERT(identifier.isStringOrSymbol());
556 if (!data.isEmpty())
557 data.resolve();
558
559 PropertyHash::Entry *e = findEntry(identifier);
560 if (e)
561 return changeMember(identifier, data, entry);
562
563 return addMemberImpl(identifier, data, entry);
564}
565
566Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
567{
568 Transition temp = { { identifier }, nullptr, int(data.all()) };
570
571 if (entry) {
572 entry->index = size;
573 entry->setterIndex = data.isAccessor() ? size + 1 : UINT_MAX;
574 entry->attributes = data;
575 }
576
577 if (t.lookup)
578 return t.lookup;
579
580 // create a new class and add it to the tree
581 Scope scope(engine);
582 Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
583 InternalClass *newClass = ic->d();
584 PropertyHash::Entry e = { identifier, newClass->size, data.isAccessor() ? newClass->size + 1 : UINT_MAX };
585 newClass->propertyTable.addEntry(e, newClass->size);
586
587 newClass->nameMap.add(newClass->size, identifier);
588 newClass->propertyData.add(newClass->size, data);
589 ++newClass->size;
590 if (data.isAccessor())
591 addDummyEntry(newClass, e);
592
593 t.lookup = newClass;
594 Q_ASSERT(t.lookup);
595 return newClass;
596}
597
598void InternalClass::removeChildEntry(InternalClass *child)
599{
601 for (auto &t : transitions) {
602 if (t.lookup == child) {
603 t.lookup = nullptr;
604 return;
605 }
606 }
607 Q_UNREACHABLE();
608
609}
610
612{
613#ifndef QT_NO_DEBUG
614 Heap::InternalClass *oldClass = object->internalClass();
615 Q_ASSERT(oldClass->findEntry(identifier) != nullptr);
616#endif
617
618 changeMember(object, identifier, Attr_Invalid);
619
620#ifndef QT_NO_DEBUG
621 // We didn't remove the data slot, just made it inaccessible.
622 // ... unless we've rebuilt the whole class. Then all the deleted properties are gone.
623 Q_ASSERT(object->internalClass()->numRedundantTransitions == 0
624 || object->internalClass()->size == oldClass->size);
625#endif
626}
627
629{
630 if (isSealed())
631 return this;
632
635
636 if (t.lookup) {
637 Q_ASSERT(t.lookup && t.lookup->isSealed());
638 return t.lookup;
639 }
640
641 Scope scope(engine);
643 Heap::InternalClass *s = ic->d();
644
645 if (!isFrozen()) { // freezing also makes all properties non-configurable
646 for (uint i = 0; i < size; ++i) {
648 if (attrs.isEmpty())
649 continue;
650 attrs.setConfigurable(false);
651 s->propertyData.set(i, attrs);
652 }
653 }
654 s->flags |= Sealed;
655
656 t.lookup = s;
657 return s;
658}
659
661{
662 if (isFrozen())
663 return this;
664
667
668 if (t.lookup) {
669 Q_ASSERT(t.lookup && t.lookup->isFrozen());
670 return t.lookup;
671 }
672
673 Scope scope(engine);
675 Heap::InternalClass *f = ic->d();
676
677 for (uint i = 0; i < size; ++i) {
679 if (attrs.isEmpty())
680 continue;
681 if (attrs.isData())
682 attrs.setWritable(false);
683 attrs.setConfigurable(false);
684 f->propertyData.set(i, attrs);
685 }
686 f->flags |= Frozen;
687
688 t.lookup = f;
689 return f;
690}
691
693{
694 // scope the intermediate result to prevent it from getting garbage collected
695 Scope scope(engine);
697 return ic->d()->nonExtensible();
698}
699
701{
702 // scope the intermediate result to prevent it from getting garbage collected
703 Scope scope(engine);
705 return ic->d()->canned();
706}
707
709{
710 if (isFrozen())
711 return true;
712
713 for (uint i = 0; i < size; ++i) {
715 if (attrs.isEmpty())
716 continue;
717 if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable())
718 return false;
719 }
720
721 return true;
722}
723
725{
726 if (isUsedAsProto())
727 return this;
728
729 Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass };
731 if (t.lookup)
732 return t.lookup;
733
734 Heap::InternalClass *newClass = engine->newClass(this);
735 newClass->flags |= UsedAsProto;
736
737 t.lookup = newClass;
738 Q_ASSERT(t.lookup);
739 return newClass;
740}
741
742static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
743{
744 if (ic->prototype == o)
745 ic->protoId = ic->engine->newProtoId();
746 for (auto &t : ic->transitions) {
747 if (t.lookup)
748 updateProtoUsage(o, t.lookup);
749 }
750}
751
752
754{
757 Q_ASSERT(!ic->prototype);
758
760}
761
763{
764 Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
765 if (ic->prototype)
766 ic->prototype->mark(stack);
767
768 if (ic->parent)
769 ic->parent->mark(stack);
770
771 ic->nameMap.mark(stack);
772}
773
774}
775
776}
777
NSData * m_data
Definition qset.h:18
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
static Heap::InternalClass * cleanInternalClass(Heap::InternalClass *orig)
static PropertyAttributes attributesFromFlags(int flags)
static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
\qmltype Particle \inqmlmodule QtQuick.Particles
@ Attr_Invalid
static const QCssKnownValue properties[NumProperties - 1]
static int grow(QLayoutStruct &ls, int delta)
static struct AttrInfo attrs[]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint object
[3]
GLfloat GLfloat f
GLenum target
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat n
GLsizei GLsizei GLchar * source
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble s
[6]
Definition qopenglext.h:235
QT_BEGIN_NAMESPACE int qPrimeForNumBits(int numBits)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned char uchar
Definition qtypes.h:27
ptrdiff_t qptrdiff
Definition qtypes.h:69
unsigned int uint
Definition qtypes.h:29
unsigned char quint8
Definition qtypes.h:41
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QLayoutItem * child
[0]
QJSEngine engine
[0]
Heap::InternalClass * internalClasses(InternalClassType icType)
Heap::InternalClass * classes[NClasses]
Heap::InternalClass * newClass(Heap::InternalClass *other)
void mark(QV4::MarkStack *markStack)
Definition qv4heap_p.h:138
Pointer< InternalClass *, 0 > internalClass
Definition qv4heap_p.h:63
bool isMarked() const
Definition qv4heap_p.h:68
Q_REQUIRED_RESULT InternalClass * locked()
Q_REQUIRED_RESULT InternalClass * cryopreserved()
static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
Q_REQUIRED_RESULT InternalClass * sealed()
SharedInternalClassData< PropertyKey > nameMap
Q_REQUIRED_RESULT InternalClass * nonExtensible()
Q_REQUIRED_RESULT InternalClass * frozen()
Q_REQUIRED_RESULT InternalClass * canned()
InternalClassTransition Transition
Q_REQUIRED_RESULT InternalClass * asProtoClass()
Q_REQUIRED_RESULT InternalClass * changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry=nullptr)
Q_QML_PRIVATE_EXPORT QString keyAt(uint index) const
std::vector< Transition > transitions
InternalClassTransition & lookupOrInsertTransition(const InternalClassTransition &t)
static void markObjects(Heap::Base *ic, MarkStack *stack)
static void removeMember(QV4::Object *object, PropertyKey identifier)
void updateProtoUsage(Heap::Object *o)
PropertyHash::Entry * findEntry(const PropertyKey id)
SharedInternalClassData< PropertyAttributes > propertyData
static Heap::MemberData * allocate(QV4::ExecutionEngine *e, uint n, Heap::MemberData *old=nullptr)
PropertyHash::Entry * entries
void addEntry(const Entry &entry, int classSize)
PropertyHashData * d
void detach(bool grow, int classSize)
bool isStringOrSymbol() const
bool isValid() const
static PropertyKey fromId(quint64 id)
static PropertyKey invalid()
ExecutionEngine * engine
static constexpr Value fromReturnedValue(ReturnedValue val)
Definition qv4value_p.h:165
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent