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 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | namespace QV4 { |
53 | |
54 | PropertyHashData::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 | |
64 | void 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 | |
81 | int 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 | |
109 | void 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 | |
133 | SharedInternalClassDataPrivate<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 | |
145 | SharedInternalClassDataPrivate<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 | |
156 | void 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 | |
165 | uint SharedInternalClassDataPrivate<PropertyKey>::alloc() const |
166 | { |
167 | return data ? data->values.alloc : 0; |
168 | } |
169 | |
170 | uint SharedInternalClassDataPrivate<PropertyKey>::size() const |
171 | { |
172 | return data ? data->values.size : 0; |
173 | } |
174 | |
175 | void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s) |
176 | { |
177 | Q_ASSERT(data && s <= alloc()); |
178 | data->values.size = s; |
179 | } |
180 | |
181 | PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) |
182 | { |
183 | Q_ASSERT(data && i < size()); |
184 | return PropertyKey::fromId(id: data->values.values[i].rawValue()); |
185 | } |
186 | |
187 | void SharedInternalClassDataPrivate<PropertyKey>::set(uint i, PropertyKey t) |
188 | { |
189 | Q_ASSERT(data && i < size()); |
190 | data->values.values[i].rawValueRef() = t.id(); |
191 | } |
192 | |
193 | void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s) |
194 | { |
195 | if (data) |
196 | data->mark(markStack: s); |
197 | } |
198 | |
199 | SharedInternalClassDataPrivate<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 | |
215 | SharedInternalClassDataPrivate<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 | |
231 | SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPrivate() |
232 | { |
233 | m_engine->memoryManager->changeUnmanagedHeapSizeUsage( |
234 | delta: -qptrdiff(m_alloc * sizeof(PropertyAttributes))); |
235 | delete [] data; |
236 | } |
237 | |
238 | void 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 | |
264 | namespace Heap { |
265 | |
266 | void 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 | |
290 | void 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 | |
312 | void 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 | |
334 | QString InternalClass::keyAt(uint index) const |
335 | { |
336 | return nameMap.at(i: index).toQString(); |
337 | } |
338 | |
339 | void 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 | |
348 | InternalClassTransition &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 | |
359 | static 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 | |
368 | Heap::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 | |
409 | Heap::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 | |
434 | Heap::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 | |
455 | Heap::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 | |
473 | void 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 | |
488 | Heap::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 | |
501 | Heap::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 | |
533 | void 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 | |
546 | void 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 | |
561 | Heap::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 | |
593 | Heap::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 | |
625 | InternalClass *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 | |
633 | InternalClass *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 | |
641 | bool 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 | |
657 | Heap::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 | |
675 | static 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 | |
686 | void 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 | |
695 | void 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 | |
708 | QT_END_NAMESPACE |
709 | |