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
40#include <qv4internalclass_p.h>
41#include <qv4string_p.h>
42#include <qv4engine_p.h>
43#include <qv4identifier_p.h>
44#include "qv4object_p.h"
45#include "qv4identifiertable_p.h"
46#include "qv4value_p.h"
47#include "qv4mm_p.h"
48#include <private/qprimefornumbits_p.h>
49
50QT_BEGIN_NAMESPACE
51
52namespace QV4 {
53
54PropertyHashData::PropertyHashData(int numBits)
55 : refCount(1)
56 , size(0)
57 , numBits(numBits)
58{
59 alloc = qPrimeForNumBits(numBits);
60 entries = (PropertyHash::Entry *)malloc(size: alloc*sizeof(PropertyHash::Entry));
61 memset(s: entries, c: 0, n: alloc*sizeof(PropertyHash::Entry));
62}
63
64void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
65{
66 // fill up to max 50%
67 bool grow = (d->alloc <= d->size*2);
68
69 if (classSize < d->size || grow)
70 detach(grow, classSize);
71
72 uint idx = entry.identifier.id() % d->alloc;
73 while (d->entries[idx].identifier.isValid()) {
74 ++idx;
75 idx %= d->alloc;
76 }
77 d->entries[idx] = entry;
78 ++d->size;
79}
80
81int PropertyHash::removeIdentifier(PropertyKey identifier, int classSize)
82{
83 int val = -1;
84 PropertyHashData *dd = new PropertyHashData(d->numBits);
85 for (int i = 0; i < d->alloc; ++i) {
86 const Entry &e = d->entries[i];
87 if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
88 continue;
89 if (e.identifier == identifier) {
90 val = e.index;
91 continue;
92 }
93 uint idx = e.identifier.id() % dd->alloc;
94 while (dd->entries[idx].identifier.isValid()) {
95 ++idx;
96 idx %= dd->alloc;
97 }
98 dd->entries[idx] = e;
99 }
100 dd->size = classSize;
101 if (!--d->refCount)
102 delete d;
103 d = dd;
104
105 Q_ASSERT(val != -1);
106 return val;
107}
108
109void PropertyHash::detach(bool grow, int classSize)
110{
111 if (d->refCount == 1 && !grow)
112 return;
113
114 PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
115 for (int i = 0; i < d->alloc; ++i) {
116 const Entry &e = d->entries[i];
117 if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
118 continue;
119 uint idx = e.identifier.id() % dd->alloc;
120 while (dd->entries[idx].identifier.isValid()) {
121 ++idx;
122 idx %= dd->alloc;
123 }
124 dd->entries[idx] = e;
125 }
126 dd->size = classSize;
127 if (!--d->refCount)
128 delete d;
129 d = dd;
130}
131
132
133SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other)
134 : refcount(1),
135 engine(other.engine),
136 data(nullptr)
137{
138 if (other.alloc()) {
139 const uint s = other.size();
140 data = MemberData::allocate(e: engine, n: other.alloc(), old: other.data);
141 setSize(s);
142 }
143}
144
145SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other,
146 uint pos, PropertyKey value)
147 : refcount(1),
148 engine(other.engine)
149{
150 data = MemberData::allocate(e: engine, n: other.alloc(), old: nullptr);
151 memcpy(dest: data, src: other.data, n: sizeof(Heap::MemberData) - sizeof(Value) + pos*sizeof(Value));
152 data->values.size = pos + 1;
153 data->values.set(e: engine, index: pos, v: Value::fromReturnedValue(val: value.id()));
154}
155
156void SharedInternalClassDataPrivate<PropertyKey>::grow()
157{
158 const uint a = alloc() * 2;
159 const uint s = size();
160 data = MemberData::allocate(e: engine, n: a, old: data);
161 setSize(s);
162 Q_ASSERT(alloc() >= a);
163}
164
165uint SharedInternalClassDataPrivate<PropertyKey>::alloc() const
166{
167 return data ? data->values.alloc : 0;
168}
169
170uint SharedInternalClassDataPrivate<PropertyKey>::size() const
171{
172 return data ? data->values.size : 0;
173}
174
175void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s)
176{
177 Q_ASSERT(data && s <= alloc());
178 data->values.size = s;
179}
180
181PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i)
182{
183 Q_ASSERT(data && i < size());
184 return PropertyKey::fromId(id: data->values.values[i].rawValue());
185}
186
187void SharedInternalClassDataPrivate<PropertyKey>::set(uint i, PropertyKey t)
188{
189 Q_ASSERT(data && i < size());
190 data->values.values[i].rawValueRef() = t.id();
191}
192
193void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s)
194{
195 if (data)
196 data->mark(markStack: s);
197}
198
199SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
200 const SharedInternalClassDataPrivate<PropertyAttributes> &other, uint pos,
201 PropertyAttributes value)
202 : refcount(1),
203 m_alloc(qMin(a: other.m_alloc, b: pos + 8)),
204 m_size(pos + 1),
205 m_engine(other.m_engine)
206{
207 Q_ASSERT(m_size <= m_alloc);
208 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(delta: m_alloc * sizeof(PropertyAttributes));
209 data = new PropertyAttributes[m_alloc];
210 if (other.data)
211 memcpy(dest: data, src: other.data, n: (m_size - 1) * sizeof(PropertyAttributes));
212 data[pos] = value;
213}
214
215SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
216 const SharedInternalClassDataPrivate<PropertyAttributes> &other)
217 : refcount(1),
218 m_alloc(other.m_alloc),
219 m_size(other.m_size),
220 m_engine(other.m_engine)
221{
222 if (m_alloc) {
223 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(delta: m_alloc * sizeof(PropertyAttributes));
224 data = new PropertyAttributes[m_alloc];
225 memcpy(dest: data, src: other.data, n: m_size*sizeof(PropertyAttributes));
226 } else {
227 data = nullptr;
228 }
229}
230
231SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPrivate()
232{
233 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
234 delta: -qptrdiff(m_alloc * sizeof(PropertyAttributes)));
235 delete [] data;
236}
237
238void SharedInternalClassDataPrivate<PropertyAttributes>::grow() {
239 uint alloc;
240 if (!m_alloc) {
241 alloc = 8;
242 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(delta: alloc * sizeof(PropertyAttributes));
243 } else {
244 // yes, signed. We don't want to deal with stuff > 2G
245 const uint currentSize = m_alloc * sizeof(PropertyAttributes);
246 if (currentSize < uint(std::numeric_limits<int>::max() / 2))
247 alloc = m_alloc * 2;
248 else
249 alloc = std::numeric_limits<int>::max() / sizeof(PropertyAttributes);
250
251 m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
252 delta: (alloc - m_alloc) * sizeof(PropertyAttributes));
253 }
254
255 auto *n = new PropertyAttributes[alloc];
256 if (data) {
257 memcpy(dest: n, src: data, n: m_alloc*sizeof(PropertyAttributes));
258 delete [] data;
259 }
260 data = n;
261 m_alloc = alloc;
262}
263
264namespace Heap {
265
266void InternalClass::init(ExecutionEngine *engine)
267{
268 Base::init();
269 new (&propertyTable) PropertyHash();
270 new (&nameMap) SharedInternalClassData<PropertyKey>(engine);
271 new (&propertyData) SharedInternalClassData<PropertyAttributes>(engine);
272 new (&transitions) std::vector<Transition>();
273
274 this->engine = engine;
275 vtable = QV4::InternalClass::staticVTable();
276// prototype = nullptr;
277// parent = nullptr;
278// size = 0;
279 extensible = true;
280 isFrozen = false;
281 isSealed = false;
282 isUsedAsProto = false;
283 protoId = engine->newProtoId();
284
285 // Also internal classes need an internal class pointer. Simply make it point to itself
286 internalClass.set(e: engine, newVal: this);
287}
288
289
290void InternalClass::init(Heap::InternalClass *other)
291{
292 Base::init();
293 new (&propertyTable) PropertyHash(other->propertyTable);
294 new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
295 new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
296 new (&transitions) std::vector<Transition>();
297
298 engine = other->engine;
299 vtable = other->vtable;
300 prototype = other->prototype;
301 parent = other;
302 size = other->size;
303 extensible = other->extensible;
304 isSealed = other->isSealed;
305 isFrozen = other->isFrozen;
306 isUsedAsProto = other->isUsedAsProto;
307 protoId = engine->newProtoId();
308
309 internalClass.set(e: engine, newVal: other->internalClass);
310}
311
312void InternalClass::destroy()
313{
314 for (const auto &t : transitions) {
315 if (t.lookup) {
316#ifndef QT_NO_DEBUG
317 Q_ASSERT(t.lookup->parent == this);
318#endif
319 t.lookup->parent = nullptr;
320 }
321 }
322
323 if (parent && parent->engine && parent->isMarked())
324 parent->removeChildEntry(child: this);
325
326 propertyTable.~PropertyHash();
327 nameMap.~SharedInternalClassData<PropertyKey>();
328 propertyData.~SharedInternalClassData<PropertyAttributes>();
329 transitions.~vector<Transition>();
330 engine = nullptr;
331 Base::destroy();
332}
333
334QString InternalClass::keyAt(uint index) const
335{
336 return nameMap.at(i: index).toQString();
337}
338
339void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
340{
341 Q_ASSERT(id.isStringOrSymbol());
342
343 Heap::InternalClass *oldClass = object->internalClass();
344 Heap::InternalClass *newClass = oldClass->changeMember(identifier: id, data, entry);
345 object->setInternalClass(newClass);
346}
347
348InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalClassTransition &t)
349{
350 std::vector<Transition>::iterator it = std::lower_bound(first: transitions.begin(), last: transitions.end(), val: t);
351 if (it != transitions.end() && *it == t) {
352 return *it;
353 } else {
354 it = transitions.insert(position: it, x: t);
355 return *it;
356 }
357}
358
359static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
360{
361 // add a dummy entry, since we need two entries for accessors
362 newClass->propertyTable.addEntry(entry: e, classSize: newClass->size);
363 newClass->nameMap.add(pos: newClass->size, value: PropertyKey::invalid());
364 newClass->propertyData.add(pos: newClass->size, value: PropertyAttributes());
365 ++newClass->size;
366}
367
368Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
369{
370 if (!data.isEmpty())
371 data.resolve();
372 PropertyHash::Entry *e = findEntry(id: identifier);
373 Q_ASSERT(e && e->index != UINT_MAX);
374 uint idx = e->index;
375 Q_ASSERT(idx != UINT_MAX);
376
377 if (entry) {
378 entry->index = idx;
379 entry->setterIndex = e->setterIndex;
380 entry->attributes = data;
381 }
382
383 if (data == propertyData.at(i: idx))
384 return static_cast<Heap::InternalClass *>(this);
385
386 Transition temp = { { .id: identifier }, .lookup: nullptr, .flags: int(data.all()) };
387 Transition &t = lookupOrInsertTransition(t: temp);
388 if (t.lookup)
389 return t.lookup;
390
391 // create a new class and add it to the tree
392 Heap::InternalClass *newClass = engine->newClass(other: this);
393 if (data.isAccessor() && e->setterIndex == UINT_MAX) {
394 Q_ASSERT(!propertyData.at(idx).isAccessor());
395
396 // add a dummy entry for the accessor
397 entry->setterIndex = newClass->size;
398 e->setterIndex = newClass->size;
399 addDummyEntry(newClass, e: *e);
400 }
401
402 newClass->propertyData.set(pos: idx, value: data);
403
404 t.lookup = newClass;
405 Q_ASSERT(t.lookup);
406 return newClass;
407}
408
409Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
410{
411 Scope scope(engine);
412 ScopedValue protectThis(scope, this);
413 if (proto)
414 proto->setUsedAsProto();
415 Q_ASSERT(prototype != proto);
416 Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
417
418 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: Transition::PrototypeChange };
419 temp.prototype = proto;
420
421 Transition &t = lookupOrInsertTransition(t: temp);
422 if (t.lookup)
423 return t.lookup;
424
425 // create a new class and add it to the tree
426 Heap::InternalClass *newClass = engine->newClass(other: this);
427 newClass->prototype = proto;
428
429 t.lookup = newClass;
430
431 return newClass;
432}
433
434Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
435{
436 Q_ASSERT(vtable != vt);
437
438 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: Transition::VTableChange };
439 temp.vtable = vt;
440
441 Transition &t = lookupOrInsertTransition(t: temp);
442 if (t.lookup)
443 return t.lookup;
444
445 // create a new class and add it to the tree
446 Heap::InternalClass *newClass = engine->newClass(other: this);
447 newClass->vtable = vt;
448
449 t.lookup = newClass;
450 Q_ASSERT(t.lookup);
451 Q_ASSERT(newClass->vtable);
452 return newClass;
453}
454
455Heap::InternalClass *InternalClass::nonExtensible()
456{
457 if (!extensible)
458 return this;
459
460 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: Transition::NotExtensible};
461 Transition &t = lookupOrInsertTransition(t: temp);
462 if (t.lookup)
463 return t.lookup;
464
465 Heap::InternalClass *newClass = engine->newClass(other: this);
466 newClass->extensible = false;
467
468 t.lookup = newClass;
469 Q_ASSERT(t.lookup);
470 return newClass;
471}
472
473void InternalClass::addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
474{
475 Q_ASSERT(id.isStringOrSymbol());
476 if (!data.isEmpty())
477 data.resolve();
478 PropertyHash::Entry *e = object->internalClass()->findEntry(id);
479 if (e) {
480 changeMember(object, id, data, entry);
481 return;
482 }
483
484 Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(identifier: id, data, entry);
485 object->setInternalClass(newClass);
486}
487
488Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
489{
490 Q_ASSERT(identifier.isStringOrSymbol());
491 if (!data.isEmpty())
492 data.resolve();
493
494 PropertyHash::Entry *e = findEntry(id: identifier);
495 if (e)
496 return changeMember(identifier, data, entry);
497
498 return addMemberImpl(identifier, data, entry);
499}
500
501Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
502{
503 Transition temp = { { .id: identifier }, .lookup: nullptr, .flags: (int)data.flags() };
504 Transition &t = lookupOrInsertTransition(t: temp);
505
506 if (entry) {
507 entry->index = size;
508 entry->setterIndex = data.isAccessor() ? size + 1 : UINT_MAX;
509 entry->attributes = data;
510 }
511
512 if (t.lookup)
513 return t.lookup;
514
515 // create a new class and add it to the tree
516 Scope scope(engine);
517 Scoped<QV4::InternalClass> ic(scope, engine->newClass(other: this));
518 InternalClass *newClass = ic->d();
519 PropertyHash::Entry e = { .identifier: identifier, .index: newClass->size, .setterIndex: data.isAccessor() ? newClass->size + 1 : UINT_MAX };
520 newClass->propertyTable.addEntry(entry: e, classSize: newClass->size);
521
522 newClass->nameMap.add(pos: newClass->size, value: identifier);
523 newClass->propertyData.add(pos: newClass->size, value: data);
524 ++newClass->size;
525 if (data.isAccessor())
526 addDummyEntry(newClass, e);
527
528 t.lookup = newClass;
529 Q_ASSERT(t.lookup);
530 return newClass;
531}
532
533void InternalClass::removeChildEntry(InternalClass *child)
534{
535 Q_ASSERT(engine);
536 for (auto &t : transitions) {
537 if (t.lookup == child) {
538 t.lookup = nullptr;
539 return;
540 }
541 }
542 Q_UNREACHABLE();
543
544}
545
546void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier)
547{
548#ifndef QT_NO_DEBUG
549 Heap::InternalClass *oldClass = object->internalClass();
550 Q_ASSERT(oldClass->findEntry(identifier) != nullptr);
551#endif
552
553 changeMember(object, id: identifier, data: Attr_Invalid);
554
555#ifndef QT_NO_DEBUG
556 // we didn't remove the data slot, just made it inaccessible
557 Q_ASSERT(object->internalClass()->size == oldClass->size);
558#endif
559}
560
561Heap::InternalClass *InternalClass::sealed()
562{
563 if (isSealed)
564 return this;
565
566 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: InternalClassTransition::Sealed };
567 Transition &t = lookupOrInsertTransition(t: temp);
568
569 if (t.lookup) {
570 Q_ASSERT(t.lookup && t.lookup->isSealed);
571 return t.lookup;
572 }
573
574 Scope scope(engine);
575 Scoped<QV4::InternalClass> ic(scope, engine->newClass(other: this));
576 Heap::InternalClass *s = ic->d();
577
578 if (!isFrozen) { // freezing also makes all properties non-configurable
579 for (uint i = 0; i < size; ++i) {
580 PropertyAttributes attrs = propertyData.at(i);
581 if (attrs.isEmpty())
582 continue;
583 attrs.setConfigurable(false);
584 s->propertyData.set(pos: i, value: attrs);
585 }
586 }
587 s->isSealed = true;
588
589 t.lookup = s;
590 return s;
591}
592
593Heap::InternalClass *InternalClass::frozen()
594{
595 if (isFrozen)
596 return this;
597
598 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: InternalClassTransition::Frozen };
599 Transition &t = lookupOrInsertTransition(t: temp);
600
601 if (t.lookup) {
602 Q_ASSERT(t.lookup && t.lookup->isFrozen);
603 return t.lookup;
604 }
605
606 Scope scope(engine);
607 Scoped<QV4::InternalClass> ic(scope, engine->newClass(other: this));
608 Heap::InternalClass *f = ic->d();
609
610 for (uint i = 0; i < size; ++i) {
611 PropertyAttributes attrs = propertyData.at(i);
612 if (attrs.isEmpty())
613 continue;
614 if (attrs.isData())
615 attrs.setWritable(false);
616 attrs.setConfigurable(false);
617 f->propertyData.set(pos: i, value: attrs);
618 }
619 f->isFrozen = true;
620
621 t.lookup = f;
622 return f;
623}
624
625InternalClass *InternalClass::canned()
626{
627 // scope the intermediate result to prevent it from getting garbage collected
628 Scope scope(engine);
629 Scoped<QV4::InternalClass> ic(scope, sealed());
630 return ic->d()->nonExtensible();
631}
632
633InternalClass *InternalClass::cryopreserved()
634{
635 // scope the intermediate result to prevent it from getting garbage collected
636 Scope scope(engine);
637 Scoped<QV4::InternalClass> ic(scope, frozen());
638 return ic->d()->canned();
639}
640
641bool InternalClass::isImplicitlyFrozen() const
642{
643 if (isFrozen)
644 return true;
645
646 for (uint i = 0; i < size; ++i) {
647 const PropertyAttributes attrs = propertyData.at(i);
648 if (attrs.isEmpty())
649 continue;
650 if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable())
651 return false;
652 }
653
654 return true;
655}
656
657Heap::InternalClass *InternalClass::asProtoClass()
658{
659 if (isUsedAsProto)
660 return this;
661
662 Transition temp = { { .id: PropertyKey::invalid() }, .lookup: nullptr, .flags: Transition::ProtoClass };
663 Transition &t = lookupOrInsertTransition(t: temp);
664 if (t.lookup)
665 return t.lookup;
666
667 Heap::InternalClass *newClass = engine->newClass(other: this);
668 newClass->isUsedAsProto = true;
669
670 t.lookup = newClass;
671 Q_ASSERT(t.lookup);
672 return newClass;
673}
674
675static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
676{
677 if (ic->prototype == o)
678 ic->protoId = ic->engine->newProtoId();
679 for (auto &t : ic->transitions) {
680 if (t.lookup)
681 updateProtoUsage(o, ic: t.lookup);
682 }
683}
684
685
686void InternalClass::updateProtoUsage(Heap::Object *o)
687{
688 Q_ASSERT(isUsedAsProto);
689 Heap::InternalClass *ic = engine->internalClasses(icType: EngineBase::Class_Empty);
690 Q_ASSERT(!ic->prototype);
691
692 Heap::updateProtoUsage(o, ic);
693}
694
695void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
696{
697 Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
698 if (ic->prototype)
699 ic->prototype->mark(markStack: stack);
700
701 ic->nameMap.mark(s: stack);
702}
703
704}
705
706}
707
708QT_END_NAMESPACE
709

source code of qtdeclarative/src/qml/jsruntime/qv4internalclass.cpp