aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4internalclass.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-05-28 15:13:05 +0200
committerUlf Hermann <ulf.hermann@qt.io>2019-05-31 12:31:14 +0200
commit9e6a8598e5ad2f826bf563ff450c911a5f8fbc2d (patch)
treed9f568941599d5d7ffaa9fbbf1d622874eae4fb0 /src/qml/jsruntime/qv4internalclass.cpp
parent8733a8762c2db473d3a39d1f01c09156c04e3772 (diff)
Clean up frozen(), sealed(), nonExtensible() and propertiesFrozen()
They all had some interesting bugs and duplicated each other: a, propertiesFrozen() changed each property individually, creating a lot of unnecessary intermediate classes. frozen() changed them all at once. b, If a class happened to contain only properties that matched the characteristics of being "sealed" or "frozen", sealed(), frozen() and propertiesFrozen() would set the flags in place and return the same class. This is bad because it violates the assumption that an InternalClass is immutable and it breaks the recursive freezing algorithm we rely on for the global object. It would stop freezing child objects at any such class, even if the children were not frozen. c, propertiesFrozen() did not set any of the flags even though it effectively sealed and froze the class. Therefore, when requesting the same class as frozen() it would iterate through all the properties again. d, frozen() implicitly also sealed the object and made it non-extensible. sealed() also implicitly made it non-extensible. This is impractical as we want to allow objects to be extensible even though all their properties are frozen. Therefore we only set the flag that belongs to each method now. We do know, however, that a frozen object is implicitly sealed. Therefore we can short-circuit this transition. Furthermore, we need to remove the assert in InternalClass::init() as you can indeed use frozen objects as prototypes for others, but that needs to be recorded in the original InternalClass via the isUsedAsProto flag. In order to set this flag, we need to perform a transition and therefore, derive from the old InternalClass. The JavaScript isFrozen() method asks for an _implicitly_, "duck typed", frozen state, which is different from what our "isFrozen" flag denotes. Therefore we add a separate const method that just checks whether all properties are frozen. Task-number: QTBUG-76033 Change-Id: I375fef83fb99035d470490fdf2348766b090831e Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4internalclass.cpp')
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp88
1 files changed, 34 insertions, 54 deletions
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index d597335031..70849775cb 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -290,7 +290,6 @@ void InternalClass::init(ExecutionEngine *engine)
void InternalClass::init(Heap::InternalClass *other)
{
Base::init();
- Q_ASSERT(!other->isFrozen);
new (&propertyTable) PropertyHash(other->propertyTable);
new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
@@ -564,22 +563,6 @@ Heap::InternalClass *InternalClass::sealed()
if (isSealed)
return this;
- bool alreadySealed = !extensible;
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- if (attrs.isConfigurable()) {
- alreadySealed = false;
- break;
- }
- }
-
- if (alreadySealed) {
- isSealed = true;
- return this;
- }
-
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
Transition &t = lookupOrInsertTransition(temp);
@@ -592,14 +575,15 @@ Heap::InternalClass *InternalClass::sealed()
Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
Heap::InternalClass *s = ic->d();
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- attrs.setConfigurable(false);
- s->propertyData.set(i, attrs);
+ if (!isFrozen) { // freezing also makes all properties non-configurable
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ attrs.setConfigurable(false);
+ s->propertyData.set(i, attrs);
+ }
}
- s->extensible = false;
s->isSealed = true;
t.lookup = s;
@@ -611,28 +595,11 @@ Heap::InternalClass *InternalClass::frozen()
if (isFrozen)
return this;
- bool alreadyFrozen = !extensible;
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) {
- alreadyFrozen = false;
- break;
- }
- }
-
- if (alreadyFrozen) {
- isSealed = true;
- isFrozen = true;
- return this;
- }
-
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen);
+ Q_ASSERT(t.lookup && t.lookup->isFrozen);
return t.lookup;
}
@@ -649,29 +616,42 @@ Heap::InternalClass *InternalClass::frozen()
attrs.setConfigurable(false);
f->propertyData.set(i, attrs);
}
- f->extensible = false;
- f->isSealed = true;
f->isFrozen = true;
t.lookup = f;
return f;
}
-Heap::InternalClass *InternalClass::propertiesFrozen()
+InternalClass *InternalClass::canned()
{
+ // scope the intermediate result to prevent it from getting garbage collected
Scope scope(engine);
- Scoped<QV4::InternalClass> frozen(scope, this);
+ Scoped<QV4::InternalClass> ic(scope, sealed());
+ return ic->d()->nonExtensible();
+}
+
+InternalClass *InternalClass::cryopreserved()
+{
+ // scope the intermediate result to prevent it from getting garbage collected
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> ic(scope, frozen());
+ return ic->d()->canned();
+}
+
+bool InternalClass::isImplicitlyFrozen() const
+{
+ if (isFrozen)
+ return true;
+
for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (!nameMap.at(i).isValid())
+ const PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
continue;
- if (!attrs.isEmpty()) {
- attrs.setWritable(false);
- attrs.setConfigurable(false);
- }
- frozen = frozen->changeMember(nameMap.at(i), attrs);
+ if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable())
+ return false;
}
- return frozen->d();
+
+ return true;
}
Heap::InternalClass *InternalClass::asProtoClass()