Diff
Modified: trunk/LayoutTests/ChangeLog (203367 => 203368)
--- trunk/LayoutTests/ChangeLog 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/LayoutTests/ChangeLog 2016-07-18 20:12:45 UTC (rev 203368)
@@ -1,3 +1,25 @@
+2016-07-17 Filip Pizlo <[email protected]>
+
+ Object.preventExtensions/seal/freeze makes code much slower
+ https://bugs.webkit.org/show_bug.cgi?id=143247
+
+ Reviewed by Michael Saboff.
+
+ These tests now run ~25x faster.
+
+ * js/regress/freeze-and-do-work-expected.txt: Added.
+ * js/regress/freeze-and-do-work.html: Added.
+ * js/regress/prevent-extensions-and-do-work-expected.txt: Added.
+ * js/regress/prevent-extensions-and-do-work.html: Added.
+ * js/regress/script-tests/freeze-and-do-work.js: Added.
+ (Foo):
+ * js/regress/script-tests/prevent-extensions-and-do-work.js: Added.
+ (Foo):
+ * js/regress/script-tests/seal-and-do-work.js: Added.
+ (Foo):
+ * js/regress/seal-and-do-work-expected.txt: Added.
+ * js/regress/seal-and-do-work.html: Added.
+
2016-07-18 Ryan Haddad <[email protected]>
Marking imported/w3c/web-platform-tests/XMLHttpRequest/event-readystatechange-loaded.htm as flaky on mac-debug WK1
Added: trunk/LayoutTests/js/regress/freeze-and-do-work-expected.txt (0 => 203368)
--- trunk/LayoutTests/js/regress/freeze-and-do-work-expected.txt (rev 0)
+++ trunk/LayoutTests/js/regress/freeze-and-do-work-expected.txt 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,10 @@
+JSRegress/freeze-and-do-work
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/js/regress/freeze-and-do-work.html (0 => 203368)
--- trunk/LayoutTests/js/regress/freeze-and-do-work.html (rev 0)
+++ trunk/LayoutTests/js/regress/freeze-and-do-work.html 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/js/regress/prevent-extensions-and-do-work-expected.txt (0 => 203368)
--- trunk/LayoutTests/js/regress/prevent-extensions-and-do-work-expected.txt (rev 0)
+++ trunk/LayoutTests/js/regress/prevent-extensions-and-do-work-expected.txt 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,10 @@
+JSRegress/prevent-extensions-and-do-work
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/js/regress/prevent-extensions-and-do-work.html (0 => 203368)
--- trunk/LayoutTests/js/regress/prevent-extensions-and-do-work.html (rev 0)
+++ trunk/LayoutTests/js/regress/prevent-extensions-and-do-work.html 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/js/regress/script-tests/freeze-and-do-work.js (0 => 203368)
--- trunk/LayoutTests/js/regress/script-tests/freeze-and-do-work.js (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/freeze-and-do-work.js 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,18 @@
+function Foo(f, g)
+{
+ this.f = f;
+ this.g = g;
+ Object.freeze(this);
+}
+
+(function() {
+ var result = 0;
+ for (var i = 0; i < 10000; ++i) {
+ var o = new Foo(i, i + 1);
+ for (var j = 0; j < 1000; ++j)
+ result += o.f * o.g;
+ }
+
+ if (result != 333333330000000)
+ throw "Error: bad result: " + result;
+})();
Added: trunk/LayoutTests/js/regress/script-tests/prevent-extensions-and-do-work.js (0 => 203368)
--- trunk/LayoutTests/js/regress/script-tests/prevent-extensions-and-do-work.js (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/prevent-extensions-and-do-work.js 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,18 @@
+function Foo(f, g)
+{
+ this.f = f;
+ this.g = g;
+ Object.preventExtensions(this);
+}
+
+(function() {
+ var result = 0;
+ for (var i = 0; i < 10000; ++i) {
+ var o = new Foo(i, i + 1);
+ for (var j = 0; j < 1000; ++j)
+ result += o.f * o.g;
+ }
+
+ if (result != 333333330000000)
+ throw "Error: bad result: " + result;
+})();
Added: trunk/LayoutTests/js/regress/script-tests/seal-and-do-work.js (0 => 203368)
--- trunk/LayoutTests/js/regress/script-tests/seal-and-do-work.js (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/seal-and-do-work.js 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,18 @@
+function Foo(f, g)
+{
+ this.f = f;
+ this.g = g;
+ Object.seal(this);
+}
+
+(function() {
+ var result = 0;
+ for (var i = 0; i < 10000; ++i) {
+ var o = new Foo(i, i + 1);
+ for (var j = 0; j < 1000; ++j)
+ result += o.f * o.g;
+ }
+
+ if (result != 333333330000000)
+ throw "Error: bad result: " + result;
+})();
Added: trunk/LayoutTests/js/regress/seal-and-do-work-expected.txt (0 => 203368)
--- trunk/LayoutTests/js/regress/seal-and-do-work-expected.txt (rev 0)
+++ trunk/LayoutTests/js/regress/seal-and-do-work-expected.txt 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,10 @@
+JSRegress/seal-and-do-work
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/js/regress/seal-and-do-work.html (0 => 203368)
--- trunk/LayoutTests/js/regress/seal-and-do-work.html (rev 0)
+++ trunk/LayoutTests/js/regress/seal-and-do-work.html 2016-07-18 20:12:45 UTC (rev 203368)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Modified: trunk/Source/_javascript_Core/ChangeLog (203367 => 203368)
--- trunk/Source/_javascript_Core/ChangeLog 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-07-18 20:12:45 UTC (rev 203368)
@@ -1,5 +1,76 @@
2016-07-17 Filip Pizlo <[email protected]>
+ Object.preventExtensions/seal/freeze makes code much slower
+ https://bugs.webkit.org/show_bug.cgi?id=143247
+
+ Reviewed by Michael Saboff.
+
+ This has been a huge pet peeve of mine for a long time, but I was always afraid of fixing
+ it because I thought that it would be hard. Well, it looks like it's not hard at all.
+
+ The problem is that you cannot mutate a structure that participates in transition caching.
+ You can only clone the structure and mutate that one. But if you do this, you have to make
+ a hard choice:
+
+ 1) Clone the structure without caching the transition. This is what the code did before
+ this change. It's the most obvious choice, but it introduces an uncacheable transition
+ that leads to an explosion of structures, which then breaks all inline caches.
+
+ 2) Perform one of the existing cacheable transitions. Cacheable transitions can either add
+ properties or they can do one of the NonPropertyTransitions, which until now have been
+ restricted to just IndexingType transitions. So, only adding transitions or making
+ certain prescribed changes to the indexing type count as cacheable transitions.
+
+ This change decouples NonPropertyTransition from IndexingType and adds three new kinds of
+ transitions: PreventExtensions, Seal, and Freeze. We have to give any cacheable transition
+ a name that fully disambiguates this transition from any other, so that the transition can
+ be cached. Since we're already giving them names in an enum, I figured that the most
+ pragmatic way to implement them is to have Structure::nonPropertyTransition() case on the
+ NonPropertyTransition and implement all of the mutations associated with that transition.
+ The alternative would have been to allow callers of nonPropertyTransition() to supply
+ something like a lambda that describes the mutation, but this seemed awkward since each
+ set of mutations has to anyway be tied to one of the NonPropertyTransition members.
+
+ This is an enormous speed-up on microbenchmarks that use Object.preventExtensions(),
+ Object.seal(), or Object.freeze(). I don't know if "real" benchmarks use these features
+ and I don't really care. This should be fast.
+
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::notifyPresenceOfIndexedAccessors):
+ (JSC::JSObject::createInitialUndecided):
+ (JSC::JSObject::createInitialInt32):
+ (JSC::JSObject::createInitialDouble):
+ (JSC::JSObject::createInitialContiguous):
+ (JSC::JSObject::convertUndecidedToInt32):
+ (JSC::JSObject::convertUndecidedToDouble):
+ (JSC::JSObject::convertUndecidedToContiguous):
+ (JSC::JSObject::convertInt32ToDouble):
+ (JSC::JSObject::convertInt32ToContiguous):
+ (JSC::JSObject::convertDoubleToContiguous):
+ (JSC::JSObject::switchToSlowPutArrayStorage):
+ * runtime/Structure.cpp:
+ (JSC::Structure::suggestedArrayStorageTransition):
+ (JSC::Structure::addPropertyTransition):
+ (JSC::Structure::toUncacheableDictionaryTransition):
+ (JSC::Structure::sealTransition):
+ (JSC::Structure::freezeTransition):
+ (JSC::Structure::preventExtensionsTransition):
+ (JSC::Structure::takePropertyTableOrCloneIfPinned):
+ (JSC::Structure::nonPropertyTransition):
+ (JSC::Structure::pin):
+ (JSC::Structure::pinForCaching):
+ (JSC::Structure::allocateRareData):
+ * runtime/Structure.h:
+ * runtime/StructureTransitionTable.h:
+ (JSC::toAttributes):
+ (JSC::changesIndexingType):
+ (JSC::newIndexingType):
+ (JSC::preventsExtensions):
+ (JSC::setsDontDeleteOnAllProperties):
+ (JSC::setsReadOnlyOnAllProperties):
+
+2016-07-17 Filip Pizlo <[email protected]>
+
RegisterSet should use a Bitmap instead of a BitVector so that it never allocates memory and is trivial to copy
https://bugs.webkit.org/show_bug.cgi?id=159863
Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (203367 => 203368)
--- trunk/Source/_javascript_Core/runtime/JSObject.cpp 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp 2016-07-18 20:12:45 UTC (rev 203368)
@@ -770,7 +770,7 @@
if (mayInterceptIndexedAccesses())
return;
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
if (!vm.prototypeMap.isPrototype(this))
return;
@@ -798,7 +798,7 @@
{
DeferGC deferGC(vm.heap);
Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
- Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided);
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateUndecided);
setStructureAndButterfly(vm, newStructure, newButterfly);
return newButterfly;
}
@@ -807,7 +807,7 @@
{
DeferGC deferGC(vm.heap);
Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
- Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32);
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32);
setStructureAndButterfly(vm, newStructure, newButterfly);
return newButterfly->contiguousInt32();
}
@@ -818,7 +818,7 @@
Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
for (unsigned i = newButterfly->vectorLength(); i--;)
newButterfly->contiguousDouble()[i] = PNaN;
- Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble);
setStructureAndButterfly(vm, newStructure, newButterfly);
return newButterfly->contiguousDouble();
}
@@ -827,7 +827,7 @@
{
DeferGC deferGC(vm.heap);
Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
- Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous);
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous);
setStructureAndButterfly(vm, newStructure, newButterfly);
return newButterfly->contiguous();
}
@@ -862,7 +862,7 @@
ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
{
ASSERT(hasUndecided(indexingType()));
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
return m_butterfly.get()->contiguousInt32();
}
@@ -874,7 +874,7 @@
for (unsigned i = butterfly->vectorLength(); i--;)
butterfly->contiguousDouble()[i] = PNaN;
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
return m_butterfly.get()->contiguousDouble();
}
@@ -881,7 +881,7 @@
ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
{
ASSERT(hasUndecided(indexingType()));
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
return m_butterfly.get()->contiguous();
}
@@ -946,7 +946,7 @@
*currentAsDouble = v.asInt32();
}
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
return m_butterfly.get()->contiguousDouble();
}
@@ -954,7 +954,7 @@
{
ASSERT(hasInt32(indexingType()));
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
return m_butterfly.get()->contiguous();
}
@@ -1002,7 +1002,7 @@
currentAsValue->setWithoutWriteBarrier(v);
}
- setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
return m_butterfly.get()->contiguous();
}
@@ -1290,24 +1290,24 @@
{
switch (indexingType()) {
case ALL_UNDECIDED_INDEXING_TYPES:
- convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
break;
case ALL_INT32_INDEXING_TYPES:
- convertInt32ToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
break;
case ALL_DOUBLE_INDEXING_TYPES:
- convertDoubleToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
break;
case ALL_CONTIGUOUS_INDEXING_TYPES:
- convertContiguousToArrayStorage(vm, AllocateSlowPutArrayStorage);
+ convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage);
break;
case NonArrayWithArrayStorage:
case ArrayWithArrayStorage: {
- Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage);
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage);
setStructure(vm, newStructure);
break;
}
Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (203367 => 203368)
--- trunk/Source/_javascript_Core/runtime/Structure.cpp 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp 2016-07-18 20:12:45 UTC (rev 203368)
@@ -435,9 +435,9 @@
NonPropertyTransition Structure::suggestedArrayStorageTransition() const
{
if (needsSlowPutIndexing())
- return AllocateSlowPutArrayStorage;
+ return NonPropertyTransition::AllocateSlowPutArrayStorage;
- return AllocateArrayStorage;
+ return NonPropertyTransition::AllocateArrayStorage;
}
Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset)
@@ -587,57 +587,19 @@
return toDictionaryTransition(vm, structure, UncachedDictionaryKind);
}
-// In future we may want to cache this transition.
Structure* Structure::sealTransition(VM& vm, Structure* structure)
{
- Structure* transition = preventExtensionsTransition(vm, structure);
-
- if (transition->propertyTable()) {
- PropertyTable::iterator end = transition->propertyTable()->end();
- for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter)
- iter->attributes |= DontDelete;
- }
-
- transition->checkOffsetConsistency();
- return transition;
+ return nonPropertyTransition(vm, structure, NonPropertyTransition::Seal);
}
-// In future we may want to cache this transition.
Structure* Structure::freezeTransition(VM& vm, Structure* structure)
{
- Structure* transition = preventExtensionsTransition(vm, structure);
-
- if (transition->propertyTable()) {
- PropertyTable::iterator iter = transition->propertyTable()->begin();
- PropertyTable::iterator end = transition->propertyTable()->end();
- if (iter != end)
- transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
- for (; iter != end; ++iter)
- iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
- }
-
- ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties());
- ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties());
- transition->checkOffsetConsistency();
- return transition;
+ return nonPropertyTransition(vm, structure, NonPropertyTransition::Freeze);
}
-// In future we may want to cache this transition.
Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure)
{
- Structure* transition = create(vm, structure);
-
- // Don't set m_offset, as one cannot transition to this.
-
- DeferGC deferGC(vm.heap);
- structure->materializePropertyMapIfNecessary(vm, deferGC);
- transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
- transition->m_offset = structure->m_offset;
- transition->setDidPreventExtensions(true);
- transition->pin();
-
- transition->checkOffsetConsistency();
- return transition;
+ return nonPropertyTransition(vm, structure, NonPropertyTransition::PreventExtensions);
}
PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm)
@@ -662,12 +624,14 @@
unsigned attributes = toAttributes(transitionKind);
IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
- if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
- if (globalObject->isOriginalArrayStructure(structure)) {
- Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType);
- if (result->indexingTypeIncludingHistory() == indexingType) {
- structure->didTransitionFromThisStructure();
- return result;
+ if (changesIndexingType(transitionKind)) {
+ if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
+ if (globalObject->isOriginalArrayStructure(structure)) {
+ Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType);
+ if (result->indexingTypeIncludingHistory() == indexingType) {
+ structure->didTransitionFromThisStructure();
+ return result;
+ }
}
}
}
@@ -679,13 +643,45 @@
return existingTransition;
}
+ DeferGC deferGC(vm.heap);
+
Structure* transition = create(vm, structure);
transition->setAttributesInPrevious(attributes);
transition->m_blob.setIndexingType(indexingType);
- transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
- transition->m_offset = structure->m_offset;
- checkOffset(transition->m_offset, transition->inlineCapacity());
+ if (preventsExtensions(transitionKind))
+ transition->setDidPreventExtensions(true);
+
+ unsigned additionalPropertyAttributes = 0;
+ if (setsDontDeleteOnAllProperties(transitionKind))
+ additionalPropertyAttributes |= DontDelete;
+ if (setsReadOnlyOnAllProperties(transitionKind))
+ additionalPropertyAttributes |= ReadOnly;
+ if (additionalPropertyAttributes) {
+ // We pin the property table on transitions that do wholesale editing of the property
+ // table, since our logic for walking the property transition chain to rematerialize the
+ // table doesn't know how to take into account such wholesale edits.
+
+ structure->materializePropertyMapIfNecessary(vm, deferGC);
+ transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
+ transition->m_offset = structure->m_offset;
+ transition->pinForCaching();
+
+ if (transition->propertyTable()) {
+ for (auto& entry : *transition->propertyTable().get())
+ entry.attributes |= additionalPropertyAttributes;
+ }
+ } else {
+ transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
+ transition->m_offset = structure->m_offset;
+ checkOffset(transition->m_offset, transition->inlineCapacity());
+ }
+
+ if (setsReadOnlyOnAllProperties(transitionKind)
+ && transition->propertyTable()
+ && !transition->propertyTable()->isEmpty())
+ transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
+
if (structure->isDictionary())
transition->pin();
else {
@@ -692,6 +688,7 @@
ConcurrentJITLocker locker(structure->m_lock);
structure->m_transitionTable.add(vm, transition);
}
+
transition->checkOffsetConsistency();
return transition;
}
@@ -817,6 +814,13 @@
m_nameInPrevious = nullptr;
}
+void Structure::pinForCaching()
+{
+ ASSERT(propertyTable());
+ setIsPinnedPropertyTable(true);
+ m_nameInPrevious = nullptr;
+}
+
void Structure::allocateRareData(VM& vm)
{
ASSERT(!hasRareData());
Modified: trunk/Source/_javascript_Core/runtime/Structure.h (203367 => 203368)
--- trunk/Source/_javascript_Core/runtime/Structure.h 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/Source/_javascript_Core/runtime/Structure.h 2016-07-18 20:12:45 UTC (rev 203368)
@@ -700,6 +700,7 @@
bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
void pin();
+ void pinForCaching();
bool isRareData(JSCell* cell) const
{
Modified: trunk/Source/_javascript_Core/runtime/StructureTransitionTable.h (203367 => 203368)
--- trunk/Source/_javascript_Core/runtime/StructureTransitionTable.h 2016-07-18 20:12:00 UTC (rev 203367)
+++ trunk/Source/_javascript_Core/runtime/StructureTransitionTable.h 2016-07-18 20:12:45 UTC (rev 203368)
@@ -40,7 +40,7 @@
// Support for attributes used to indicate transitions not related to properties.
// If any of these are used, the string portion of the key should be 0.
-enum NonPropertyTransition {
+enum class NonPropertyTransition : unsigned {
AllocateUndecided,
AllocateInt32,
AllocateDouble,
@@ -48,46 +48,98 @@
AllocateArrayStorage,
AllocateSlowPutArrayStorage,
SwitchToSlowPutArrayStorage,
- AddIndexedAccessors
+ AddIndexedAccessors,
+ PreventExtensions,
+ Seal,
+ Freeze
};
inline unsigned toAttributes(NonPropertyTransition transition)
{
- return transition + FirstInternalAttribute;
+ return static_cast<unsigned>(transition) + FirstInternalAttribute;
}
+inline bool changesIndexingType(NonPropertyTransition transition)
+{
+ switch (transition) {
+ case NonPropertyTransition::AllocateUndecided:
+ case NonPropertyTransition::AllocateInt32:
+ case NonPropertyTransition::AllocateDouble:
+ case NonPropertyTransition::AllocateContiguous:
+ case NonPropertyTransition::AllocateArrayStorage:
+ case NonPropertyTransition::AllocateSlowPutArrayStorage:
+ case NonPropertyTransition::SwitchToSlowPutArrayStorage:
+ case NonPropertyTransition::AddIndexedAccessors:
+ return true;
+ default:
+ return false;
+ }
+}
+
inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition)
{
switch (transition) {
- case AllocateUndecided:
+ case NonPropertyTransition::AllocateUndecided:
ASSERT(!hasIndexedProperties(oldType));
return oldType | UndecidedShape;
- case AllocateInt32:
+ case NonPropertyTransition::AllocateInt32:
ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType));
return (oldType & ~IndexingShapeMask) | Int32Shape;
- case AllocateDouble:
+ case NonPropertyTransition::AllocateDouble:
ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType));
return (oldType & ~IndexingShapeMask) | DoubleShape;
- case AllocateContiguous:
+ case NonPropertyTransition::AllocateContiguous:
ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType));
return (oldType & ~IndexingShapeMask) | ContiguousShape;
- case AllocateArrayStorage:
+ case NonPropertyTransition::AllocateArrayStorage:
ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType));
return (oldType & ~IndexingShapeMask) | ArrayStorageShape;
- case AllocateSlowPutArrayStorage:
+ case NonPropertyTransition::AllocateSlowPutArrayStorage:
ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType));
return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
- case SwitchToSlowPutArrayStorage:
+ case NonPropertyTransition::SwitchToSlowPutArrayStorage:
ASSERT(hasArrayStorage(oldType));
return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
- case AddIndexedAccessors:
+ case NonPropertyTransition::AddIndexedAccessors:
return oldType | MayHaveIndexedAccessors;
default:
- RELEASE_ASSERT_NOT_REACHED();
return oldType;
}
}
+inline bool preventsExtensions(NonPropertyTransition transition)
+{
+ switch (transition) {
+ case NonPropertyTransition::PreventExtensions:
+ case NonPropertyTransition::Seal:
+ case NonPropertyTransition::Freeze:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline bool setsDontDeleteOnAllProperties(NonPropertyTransition transition)
+{
+ switch (transition) {
+ case NonPropertyTransition::Seal:
+ case NonPropertyTransition::Freeze:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline bool setsReadOnlyOnAllProperties(NonPropertyTransition transition)
+{
+ switch (transition) {
+ case NonPropertyTransition::Freeze:
+ return true;
+ default:
+ return false;
+ }
+}
+
class StructureTransitionTable {
static const intptr_t UsingSingleSlotFlag = 1;